Avdeling for informatikk og e-læring, Høgskolen i Sør-Trøndelag Objektorientering i VB videregående Oppdatert av Atle Nes Objektorientering i VB videregående Resymé: Denne leksjonen tar opp flere detaljer knyttet til bruk av klasser. Et av de viktigste prinsippene innen objektorientering, er innkapsling. Hva er det, og hvordan gjøres det i praksis? 10.1. PROBLEMATIKKEN KNYTTET TIL INNKAPSLING... 2 10.1.1. Eksempel på bruk av Public objektvariabler... 2 10.1.2. Public objektvariabler er ikke god programmeringsskikk... 3 10.2. MULIG TILNÆRMING NR 1 OFFENTLIGE METODER... 4 10.2.1. Egen metode for å sette objektvariabler... 4 10.2.2. Forklaring til metoden lagreinformasjon()... 5 10.2.3. Bruk av metoden lagreinformasjon()... 5 10.2.4. Bruk av mange metoder for å lagre og hente informasjon... 7 10.2.5. Metoder kan gjøre det du vil også validering... 8 10.2.6. Oppsummert: Hvorfor metoder?... 10 10.3. MULIG TILNÆRMING NR 2 KONSTRUKTØRER... 10 10.3.1. Hva skjer når vi lager et nytt objekt?... 10 10.3.2. Konstruktører lager og fyller objektet på en gang... 11 10.3.3. En enda kortere syntaks... 12 10.3.4. Overloading... 13 10.4. MULIG TILNÆRMING NR 3 BRUK AV PROPERTY... 14 10.4.1. Property brukes primært til å hente og endre verdi i objektvariabler... 14 10.4.2. Forklaring til Get og Set... 15 10.4.3. Detaljer og ytterligere forklaring... 16 10.4.4. Hva har en skarve TextBox og en Label å gjøre med en Aha-opplevelse?... 16 10.4.5. En siste Aha-opplevelse... 18
Objektorientering i VB videregående side 2 av 19 10.1. Problematikken knyttet til innkapsling Vi så i leksjonen om grunnleggende objektorientering, at en klasse har objektvariabler (egenskaper) og metoder. Et eksempel på en klasse er Person, og denne personen har kanskje en vekt, et navn og en høyde. Det er mulig å finne en såkalt body mass index til personen basert på en utregning av vekt deltpå høyden (i meter) opphøyd i andre. I stedet for å registrere body mass index, passer det å lage en metode som regner denne ut ved behov. Basisinformasjon om vekt og høyde kan også brukes til mye annet rart. Vi skal nå se hvordan vi kan gjøre koden vår mer modulær og selvstendig. Dette er et ønske innen objektorientering. 10.1.1. Eksempel på bruk av Public objektvariabler Dersom vi ikke tar med noen metoder, vil klassen Person kunne se slik ut: Public Class Person Public navn As String 'navnet på personen Public vekt As Double 'antall kg Public hoyde As Double 'antall cm 'Tar ikke med noen metoder her for oversiktens skyld End Class Husk at en klasse gjerne ligger i en egen fil, og uansett mellom kodeordene Public Class KlassensNavn og End Class. Siden vi har angitt kodeordet Public foran objektvariablene navn, vekt og hoyde, vil disse objektvariablene kunne brukes utenfor filen (faktisk også på tvers av prosjekter om en vil det, så lenge prosjektene ligger i samme solution). Det betyr at vi kan skrive følgende under for eksempel Button1 i Form1: Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click 'Lager en ny instans av klassen Person Dim lise As Person lise = New Person 'Registrerer navn, høyde og vekt for Lise lise.vekt = 62 lise.navn = "Lise Nordmann" lise.hoyde = 170 MsgBox(lise.navn & " er " & lise.hoyde & " høy") Her har vi laget et nytt objekt som heter lise. Lise må ha en alder, et navn og en høyde. Det er nødvendig å fylle objektet lise sine objektvariabler med informasjon. Etter å ha gjort dette, henter vi ut informasjon om objektet (navn og høyde) og skriver ut i en meldingsboks.
Objektorientering i VB videregående side 3 av 19 Figur 1: Objektet lise (til høyre) som er en instans av den generelle klassen Person (til venstre). 10.1.2. Public objektvariabler er ikke god programmeringsskikk Vi kan bruke punktumnotasjonen objekt.objektvariabel = "verdi" for å foreta en tilordning til en objektvariabel. Dette fungerer i vårt eksempel ene og alene fordi vi har satt Public foran navnet på objektvariablene i klassen Person. Dersom vi hadde satt Dim navn As String, så ville det ikke fungert å skrive lise.navn = "Lise Nordmann" under Button1_Click. Dim er en såkalt modifikator som angir en lokal variabel, mens Public altså er en modifikator som gjør at en variabel (eller en klasse eller en metode/funksjon) blir tilgjengelig overalt.! Her er et interessant apropos for den som er interessert. Private er nemlig en mellomting mellom Public og Dim. Private betyr dermed at variabelen er tilgjengelig overalt innenfor klassen, men ikke utenfor. Det var forresten slik vi kunne lage globale variabler, matriser og funksjoner tidligere i kurset. Vi satte da kodeordet Private foran globale variabler, for eksempel slik: Private matrise(4) as Integer. Vi plasserte slike globale ting øverst i koden, før alle knappene våre. Den observante leser har trolig sett at det i alle programmer vi har laget til nå i hele kurset, helt øverst har stått Public Class Form1, og helt nederst har stått en tilsvarende End Class. Det vil si at når vi lager en knapp ved å dobbeltklikke på koden til Form1, så er vi egentlig bare kommet til koden som angir klassen Form1... Alt av kode til knappene våre etc. ligger i denne klassen, og dermed vil Private-variabler få en global funksjon. Så tilbake til problemstillingen: Vi kan deklarere objektvariablene som Public, og vi får da tilgang til å endre og sette disse fra utsiden. Det er derimot noen ulemper med en slik fremgangsmåte: Dersom vi senere ønsker å endre klassens objektvariabler til å hete noe annet, for eksempel ikke navn, men fornavn, så betyr det at vi må endre koden også i Button1_Click. Klassen har plutselig en mindre smart avhengighet til der hvor objektene brukes i praksis. Validering av data overlates til den som programmerer objektene, ikke den som har programmert klassen. Dersom noen forsøker å lage et nytt personobjekt og gi en feilaktig verdi, som vist her, vil programmet kræsje siden klassen forventer en Doubleverdi: Dim kari as Person kari = New Person kari.vekt = "vet ikke" 'her kræsjer programmet fordi datatypen er feil
Objektorientering i VB videregående side 4 av 19 Generelt gjelder prinsippet innen objektorientering at en skal forsøke å innkapsle koden. Klassen Person beskriver karakteristika og aksjoner som gjelder for en generell person. Det er om å gjøre å holde dette så generelt som mulig. En bør derfor unngå å kreve at objektvariabler refereres til direkte fra koden der hvor objektene lages og brukes. Likevel er det selvsagt nødvendig å si at for eksempel Ole er 83 kg og 192 cm. Hvis ikke, kan vi ikke finne ut hva Ole sin body mass index er, og vi kan heller ikke gjøre noe annet fornuftig med Ole. Det er altså essensielt å kunne endre objektvariablene til et objekt, uten å måtte bruke Public-variabler i klassens kode. Hvordan får vi til dette? Figur 2: Ønsker at koden i for eksempel Button1_Click skal ha mulighet til å få lagret informasjon inn i objektet ole, uten å bryte med prinspper rundt innkapsling. 10.2. Mulig tilnærming nr 1 offentlige metoder En klasse kan ha objektvariabler og metoder. I stedet for å bruke Public objektvariabler, og sette disse der objektene brukes ved hjelp av ole.vekt = 83, kan vi jo lage lages en metode som får i oppdrag å sette Ole sin vekt. Metoder skal vanligvis kunne kalles opp utenfra klassen, og disse er derfor stort sett deklarert som Public. Selve metoden ligger inne i klassen, og det betyr at den har tilgang til alle Private-variabler. Det betyr at vi kan lage oss en Public-metode, og la den få i oppdrag å registrere verdier i objektvariablene! 10.2.1. Egen metode for å sette objektvariabler La oss nå lage en ny, modifisert og bedre utgave av klassen Person: Public Class Person Private navn As String 'objektvariablene er kun tilgjengelige Private vekt As Double 'internt i denne klassen Private hoyde As Double 'siden de er deklarert som Private Public Sub lagreinformasjon(byval navn As String, _ ByVal kg As Double, _ ByVal cm As Double) 'Metode for å registrere data om navn, vekt og høyde. 'Fyller objektvariablene med innholdet som kommer fra argumentene vekt = kg 'argumentet heter "kg" mens objektvariabelen heter "vekt" hoyde = cm Me.navn = navn 'dersom argumentet og objektvariabelen heter det samme, 'må kodeordet Me. brukes for å angi objektvariabelen End Class
Objektorientering i VB videregående side 5 av 19 Her har vi laget en metode som har én eneste oppgave i livet: Motta informasjon om et navn, en vekt og en høyde. Plassere denne informasjonen inn i dette objektets tilsvarende objektvariabler. 10.2.2. Forklaring til metoden lagreinformasjon() Det er noen detaljer som bør forklares i koden til metoden lagreinformasjon() Vi har brukt kodeordet Sub, siden dette er en metode som ikke skal returnere noen verdi. Vi kunne brukt Function, men da skal vi egentlig returnere en verdi, og det er ikke nødvendig her, så da bruker vi helst Sub. Metoden har tre argumenter. Disse heter navn, kg og cm. Dette er på en måte inngangsporten for informasjon som skal inn til metoden og behandles der inne. Hvis alt er på en laaaaang linje, så går det greit. Dersom du vil bryte på flere linjer, må understrek-tegnet brukes (som vi har gjort). Figur 3: Visual Studio hjelper til ved bruk av metoder. Her vises hvilke argumenter (og datatyper) som må fylles ut. Det metoden skal gjøre, er å plassere informasjonen som kommer fra hvert av argumentene inn i riktig objektvariabel. Objektvariabelen vekt skal få den verdien som sendes gjennom argumentet kg. Objektvariabelen hoyde skal få den verdien som sendes gjennom argumentet cm. Merk at objektvariabelen navn skal få den verdien som sendes inn gjennom argumentet navn. Problemet er at begge heter navn. Her har vi en situasjon som må håndteres. Hvordan skal VB vite hva som er argumentet, og hva som er objektvariabel i den koden som kommer inne i selve metoden? Måten å skille dem fra hverandre på, er å bruke kodeordet Me og et punktum foran objektvariabelen. Me er nemlig et kodeord som henspeiler tilbake på akkurat dette objektet. Hvorfor brukes Me. kun i den siste setningen? Fordi de andre to argumentene ikke har samme navn som objektvariablene, og derfor er det ingen konflikt ute og går. Hva skal en så bruke? Dette er helt valgfritt. I stedet for å pønske ut synonymer av objektvariablene som navn på argumentene, kan en med fordel bruke Me. Det er egentlig en smakssak, men Me er såpass vanlig at du bør kjenne til muligheten slik at du forstår andres kode. Det siste som er verdt å merke seg, er at denne metoden er deklarert som Public. Det er veldig viktig, og betyr at den kan kalles opp fra et helt annet sted, for eksempel under Button1_Click i Form1. Det er nettopp dette vi ønsker. Public er helt nødvendig å bruke for å angi korrekt tilgang til denne metoden. 10.2.3. Bruk av metoden lagreinformasjon() La oss nå se hvordan metoden lagreinformasjon() kan brukes i praksis. Vi har her ganske enkelt dobbeltklikket på en knapp i Form1, og kommer da til koden bak
Objektorientering i VB videregående side 6 av 19 Button1_Click(...). Vi oppretter to objekter for å tydeliggjøre at hvert objekt er uavhengig av hverandre (forskjellige instanser av samme klasse). Public Class Form1 'Program som bruker klassen Person og lager to personer Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click 'Lager to objektvariabler av type Person Dim lise As Person lise = New Person Dim ole As Person ole = New Person 'Registrerer navn, vekt og høyde for Ole ole.lagreinformasjon("ole Nordmann", 83, 192) 'Registrerer navn, vekt og høyde for Lise lise.lagreinformasjon("lise Nordmann", 62, 170) End Class Legg merke til forskjellen på hvordan objektvariablene navn, vekt og hoyde får innhold denne gangen, sammenliknet med tidligere. Vi ønsker å registrere objektet ole sitt navn, vekt og høyde. Vi har ikke tilgang til objektvariablene som ligger inne i objektet, siden disse er lokale (deklarert med Private). Vi har derimot en metode som kan gjøre jobben for oss, og kaller derfor opp lagreinformasjon() med de riktige verdiene som argumenter. Figur 4: Illustrasjon over dataflyten når Public-metoden lagreinformasjon() kalles opp i Button1_Click. Tenk over at objektet er en instans av klassen Person. Objektvariablene er
Objektorientering i VB videregående side 7 av 19 Private, men siden metoden ligger i samme klasse, har den tilgang til å skrive til objektvariablene. Det at objektvariabler gjerne deklareres i klassen som Private, mens metoder deklareres som Public, er veldig ofte tilfelle innen OOP-løsninger. Objektets variabler blir da lokale, og eksponeres ikke noe annet sted enn internt i objektet. Metodene blir derimot offentlige, og eksponeres utenfor objektet. Det betyr at vi kan lage metoder som lar oss fra utsiden hente ut eller endre den tilsynelatende utilgjengelige informasjonen i objektvariablene. Dette prinsippet er viktig å forstå. Ikke fall for fristelsen å plassere Public overalt bare for at det fungerer. Det vil skape problemer i lengden. Prøv heller å forstå betydningen av disse modifikatorene først som sist. 10.2.4. Bruk av mange metoder for å lagre og hente informasjon Vi har nå sett på hvordan vi kan lage en metode for å registrere informasjon i et objekt (i objektvariablene). Vår metode registrerte informasjon i tre objektvvariabler i samme sleng. Det er ingenting i veien for å ha flere metoder som registrerer informasjon. Vi kunne tilsvarende hatt metoder for å lese ut verdien av objektvariablene. Om vi følger en slik kodestil, vil vi få dobbelt så mange metoder som antall private objektvariabler (altså 6 i vårt tilfelle). Public Class Person Private navn As String 'objektvariablene er kun tilgjengelige Private vekt As Double 'internt i denne klassen Private hoyde As Double 'siden de er deklarert som Private Public Sub lagrenavn(byval navn As String) 'Metode for å registrere navnet i riktig objektvariabel Me.navn = navn Public Sub lagrevekt(byval vekt As Double) Me.vekt = vekt Public Sub lagrehoyde(byval hoyde As Double) Me.hoyde = hoyde Public Function hentnavn() as String return navn End Function Public Function hentvekt() as Double return vekt End Function Public Function henthoyde() as Double return hoyde End Function End Class Legg merke til hvordan de tre første metodene er deklarert som Sub. Disse skal lagre det som kommer inn i argumentet i riktig objektvariabel. De tre siste metodene er derimot deklarert som Function. Disse skal returnere verdien til de ulike objektvariablene. Legg
Objektorientering i VB videregående side 8 av 19 merke til at returtypen for hver metode er satt til det samme som typen på tilhørende objektvariabel. Figur 5 viser eksempel på kode som bruker av disse 6 metodene til å lagre og lese informasjonen i objektvariablene: Figur 5: Kode bak Button2_Click bruker metoder som er definert i klassen, til å lagre informasjon i objektet knut og hente ut informasjon fra objektet knut. 10.2.5. Metoder kan gjøre det du vil også validering Nå tenker du kanskje: Dette er jo veldig tungvindt. Hvorfor bruke OOP når det blir så utrolig mye mer kode å skrive, og så mange ting å holde orden på? Fordelene med OOP er mange. Joda, det er riktig at det kan bli mye kode for å løse et lite problem, men det blir faktisk motsatt (mye mindre kode) når du skal løse større problemer. I tillegg vil du oppleve gevinster som går på sikkerhet, modularitet, gjenbrukbarhet og liknende. Du vil kunne bli mer en komponent-programmerer enn en detalj-programmerer. Det øker mulighetene for bedre produktivitet, effektivitet, kvalitet, sikkerhet og så videre. Med så mye kode å skrive (to metoder for hver objektvariabel som er Private) kan det være fristende å lage objektvariablene som Public og slik spare plass. La oss gå gjennom et eksempel som tar opp et viktig motargument mot å gjøre det så lett som mulig. Tenk deg at du i knappen Button3_Click i Form1 ønsker å opprette et nytt objekt. Det er personen Karl Anton det er snakk om. Du lager objektet ka og vil så registrere høyden på ka. Du skriver inn 1.80 i koden fordi Karl Anton er 1 meter og 80 cm høy. Problemet er bare at objektvariabelen hoyde bør ha høyden i antall cm. Hvorfor? Fordi metoden finnbmi() som brukes til å regne ut body mass index (BMI) forventer at høyden som er lagret i objektet, er oppgitt i antall cm. Denne metoden tar nemlig høyden og deler på 100. Dersom du ved en feiltakelse registrerer høyden som antall meter i stedet for antall centimeter, blir det feil å dele på 100 i metoden.
Objektorientering i VB videregående side 9 av 19 Figur 6: Metoden finnbmi() tar antall cm og deler på 100, før den regner ut BMI. Denne situasjonen er skummel. Objektvariabelen hoyde er av type Double, så det går bra å registrere både 180 og 1.80. Hvis en registrerer 180, vil alt gå bra i det en kaller opp metoden finnbmi(). Hvis en registrerer 1.80, vil finnbmi() klare å regne ut en BMI, men verdien vil være veldig feil. Vi kan skrive om metoden til å ikke dele på 100, men da har vi akkurat samme situasjon: en person som glemmer seg og skriver 180 i stedet for 1.80, vil da oppleve feilen. Det som er lurt her, er å validere at verdien er riktig før objektvariabelen får innhold. Vi kan validere på to steder: I Button3_Click rett før metoden kalles opp. Vi kaller bare opp metoden dersom tallet er riktig skrevet inn. Dette får vi til med en If-test. I Klassen Person, nærmere bestemt i metoden lagrehoyde(). Dette får vi til med en If-test. La oss nå se hvordan vi kan faktisk tillate brukeren å skrive enten antall meter eller antall centimeter! Public Class Person Private navn As String 'objektvariablene er kun tilgjengelige Private vekt As Double 'internt i denne klassen Private hoyde As Double 'siden de er deklarert som Private 'De andre metodene er fjernet her for oversiktens skyld. Public Sub lagrehoyde(byval hoyde As Double) 'Antar at meter brukes hvis tallet er lite If hoyde < 10 Then Me.hoyde = hoyde * 100 Else Me.hoyde = hoyde 'Hvis stort tall, så lagres tallet direkte End If End Class Dersom vi nå kaller opp metoden slik: Dim ka As New Person ka.lagrehoyde(1.80) ka.lagrevekt(78) MsgBox("BMI-verdien til Karl Anton er " & ka.finnbmi())
Objektorientering i VB videregående side 10 av 19 så vil resultatet bli at BMI-verdien er ca. 24,07. Dersom vi kaller med ka.lagrehoyde(180) så får vi samme resultat. Hvis vi ikke hadde foretatt validering, ville verdien blitt ca 240740, altså et helt feil tall (regn ut selv om du er i tvil). 10.2.6. Oppsummert: Hvorfor metoder? Fordelen med metode-fremgangsmåten blir åpenbar i følgende scenario: 1. Du har først en metode som setter vekt direkte 2. Du lager et program med mange knapper i mange forms. Rundt om kring i disse har du behov for å lage nye personer, for eksempel Petra og Trulte. Du oppretter typisk petra og kaller opp metoden petra.lagrevekt(100) for å få plassert inn riktig vekt i objektvariabelen til objektet petra. Du skal så lagre for objektet trulte, og skriver trulte.lagrevekt(859). Du skulle egentlig skrive 85.9 men, glemte kommaet (punktumet). 3. Du oppdager feilen med en gang, og tenker at jeg kan jo rette opp til å være 85.9, men hva om jeg gjør samme feil senere? La meg heller fikse metoden lagrevekt() til å ta hensyn til potensielt feil registrering. 4. Du åpner filen som har klassen Person, og lokaliserer metoden lagrevekt(). Du legger til en If-test inne i den, og vips, så har du fikset alle fremtidige feiltastinger. 5. Det fine med denne fiksen, er at du ikke trenger å endre noe annet sted enn i metoden. Der metoden kalles fra (i ulike knapper i ulike forms) har en hele tiden antatt at ting fungerer, uten å bry seg med detaljene i koden bak klassens metode. Det er nettopp dette OOP handler om å skjule detaljene. Dersom du fikser detaljene, så skal det helst ikke gi konsekvenser for resten av programmet der klassen instansieres i form av objekter. Du får altså økt fleksibilitet ved å la metoder sette verdiene i objektvariablene, i stedet for å sette verdiene direkte ved å la objektvariablene være offentlige (Public). 10.3. Mulig tilnærming nr 2 Konstruktører Det er vanlig å bruke konstruktører for å innkapsle objektvariablene. Konstruktører gjør det enkelt for oss å opprette et nytt objekt av en klasse (instans) og fylle dette objektet med innhold på samme tid. Det er mulig å ha flere konstruktører for hver klasse. La oss se nærmere på hva en konstruktør er, og motivasjonen bak. 10.3.1. Hva skjer når vi lager et nytt objekt? Når vi skal lage et nytt objekt, bruker vi kodeordet New. Vi kan for eksempel skrive slik: Dim elisabeth as Person elisabeth = New Person I første setning lager vi en liten boks i minnet som kan inneholde adresen til et objekt av type Person. I andre setning lager vi et nytt objekt, og lagrer referansen til dette objektet i boksen som heter elisabeth. I stedet for å si at elisabeth nå er boksen med referansen som henviser til objektet elisabeth, sier vi kort og godt at elisabeth er objektet elisabeth. Det er mye enklere å snakke om objekter direkte, selv om det egentlig bare er en referanse til objektet vi snakker om. Den øverste delen av Figur 7 viser et objekt slik vi normalt tenker på det, mens den nederste delen viser hvordan dette objektet egentlig er representert i minnet i
Objektorientering i VB videregående side 11 av 19 maskinen: Boksen som heter elisabeth (og som vi for enkelhetsskyld kaller objektet elisabeth) har en verdi (referanse) som viser til det stedet i minnet hvor objektet befinner seg. Figur 7: Objektet elisabeth slik vi kan visualisere oss det (gul figur, øverst) og slik det faktisk representeres inne i maskinen (blå, nederst). 8F6H4003 er adressen til det stedet i minnet hvor objektet ligger lagret. Når vi oppretter et objekt får vi en kopi av den tilhørende klassen. I Figur 7 vises hvordan et nytt objekt har de tre objektvariablene vekt, navn og hoyde, men at disse mangler innhold. Det neste logiske steget er som regel å fylle objektet med innhold. Veldig ofte er nettopp dette steg 2 noe vi ønsker å gjøre rett etter å ha opprettet objektet. Kodeordet New kan brukes til å lage et objekt, og så kan vi for eksempel gi objektvariablene verdier ved å kalle opp respektive metoder. Hva om vi kunne slå sammen disse to operasjonene til én? 10.3.2. Konstruktører lager og fyller objektet på en gang Når vi lager et objekt, så kan vi si at vi konstruerer objektet (engelsk: to construct an object). Innen OOP har vi en spesiell type metode som heter konstruktør (engelsk: constructor). Denne har i Visual Basic alltid navnet New, og kan deklareres slik i klassen vår: Public Class Person Private navn As String 'objektvariablene er kun tilgjengelige Private vekt As Double 'internt i denne klassen Private hoyde As Double 'siden de er deklarert som Private 'Her er en Constructor for å registrere en ny person Public Sub New(ByVal etnavn As String, _ ByVal envekt As Double, _ ByVal enhoyde As Double) navn = etnavn vekt = envekt hoyde = enhoyde
Objektorientering i VB videregående side 12 av 19 Public Function finnbmi() As Double 'Regner her ut body mass index Dim meter As Double meter = hoyde / 100 'fordi vi skal regne i antall meter, ikke i cm Return vekt/(meter*meter) 'parentesene regnes ut først End Function End Class Konstruktøren er deklarert som Public, og den kan dermed kalles utenfor klassen. Siden konsturktøren befinner seg inne i klassen, har den tilgang til alle objektvariablene som er deklarert med Private. La oss se på bruken av konstruktøren før vi forklarer fullstendig ferdig : Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click 'Lager et objekt av type Person Dim bmi As Double Dim alex As Person alex = New Person("Aleksander", 100, 200) 'Skriver ut verdien i objektvariabelen vekt vha Property Vekten bmi = alex.finnbmi() MsgBox("BMI-en er: " & bmi) Vi lager et objekt (alex) og setter: alex = New Person("Aleksander", 100, 200) Siden vi nå har en konstruktør for klassen som krever 3 argumenter, angir vi 3 argumentverdier i dette kallet. Dermed sendes det 3 argumenter inn til konstruktøren som definert med New i klassen Person. Se nå tilbake på koden for konstruktøren, og du ser at det som mottas heter henholdsvis etnavn, envekt og enhoyde. Disse har nå henholdsvis verdiene Aleksander, 100 og 200. For å få Aleksander inn i objektvariabelen navn, skriver vi bare navn = etnavn inne i konstruktøren. Tilsvarende fylles de andre objektvariablene med riktige verdier. Nå har vi i praksis et objekt som ser slik ut: Figur 8: Etter at konstruktøren er benyttet, har objektet fått en rekke verdier. 10.3.3. En enda kortere syntaks Det er mulig å skrive enda mer kompakt når vi lager objekter, enn å måtte bruke to linjer. I stedet for: Dim alex As Person
Objektorientering i VB videregående side 13 av 19 alex = New Person("Aleksander", 100, 200) kan vi skrive Dim alex As New Person("Aleksander", 100, 200) Dermed er det mulig å lage virtuelle utgaver av alle sine slektninger på kort tid, som vist i Button2_Click her: Private Sub Button2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button2.Click Dim alex As New Person("Aleksander", 100, 200) Dim jeppe As New Person("Jens Peder", 78, 190) Dim kanin As New Person("Kari Nina", 52, 167) Dim ole As New Person("Ole Bernt Kånrad", 124.78, 197.50) Dim berith As New Person("Berit Hjørdis", 60.25, 170) Dim leffen As New Person("Leif Espen", 59, 156) Vi kunne nå funnet BMI på hver enkelt av disse, hvis ønskelig. Du ser at ved bruk av konstruktører får vi laget objekter og fylt disse med innhold på en rask måte. Dessuten sikrer vi oss at vi ikke glemmer å registrere objektvariablene. Konstruktører er bra å bruke. 10.3.4. Overloading Helt til slutt nevnes et begrep som heter overloading (på engelsk). Du har tidligere sett at metoder kan ha så mange argumenter du selv måtte ønske. Nå har vi nettopp laget en konstruktør som fyller inn navn, vekt og høyde på en person. Hva om vi ønsker å lage et objekt som bare har noen standardverdier, for eksempel Ola Nordmann som navn, 85 kg som vekt og 180 som høyde? Vi kan enkelt lage en konstruktør som gjør dette: Public Sub New() navn = "Ola Nordmann" vekt = 85 hoyde = 180 Det fine er at denne konstruktøren kan leve side om side med den første konstruktøren vi lagde. Vi kan altså ha flere konstruktører i en og samme klasse! Dette er en fin mulighet, og det øker fleksibiliteten. Figur 9 og Figur 10 viser eksempel på opprettelse av et nytt objekt (i for eksempel Button1_Click) basert på klassen Person. I det vi skriver Person( hjelper utviklingsmiljøet oss og avslører at det fins to tilgjengelige konstruktører. Disse vises i form av en liste, og vi kan se argumentene og datatypene som forventes for hver konstruktør. Figur 9: Utviklingsmiljøet hjelper oss og antyder at den første konstruktøren krever tre argumenter.
Objektorientering i VB videregående side 14 av 19 Figur 10: Utviklingsmiljøet viser den andre konstruktøren når vi trykker pil ned. Den andre krever ingen argumenter. Det er vanlig at ferdiglagde.net-klasser har mange konstruktører som en kan velge mellom. Det er alltid lurt å lese argument-listen for å få en indikasjon av hva som kan gjøres. Begge formene er mulige å bruke: Dim svend as New Person("Svend", 88, 196) Dim ola as New Person() Som følge av den første linjen, opprettes objektet svend, og det får objektvariablene fylt med Svend, 88 og 196. Den andre linjen lager et objekt som heter ola, og dette får verdiene Ola Nordmann, 85 og 180. Dette fenomenet at vi kan ha potensielt mange konstruktører gjelder mer generelt også. Det er nemlig mulig å lage metoder som har samme navn, så lenge antall argumenter varierer. Vi skal ikke gå nærmere inn på dette. Et lite apropos: Egentlig trenger en ikke skrive parenteser etter Person i den siste linjen. VB tillater både med og uten parentes dersom argumentlisten er tom. 10.4. Mulig tilnærming nr 3 bruk av Property Det er flott å kunne bruke metoder for å lagre informasjon i en objektvariabel, og for å hente ut informasjon fra en objektvariabel. Det er derimot litt mer tungvindt å måtte kalle en metode enn å kunne bruke syntaksen som vist aller først i denne leksjonen: knut.vekt = 76 Den gode nyheten er at det faktisk er mulig å skrive på denne måten, og samtidig beholde objektvariablene med modifikatoren Private. 10.4.1. Property brukes primært til å hente og endre verdi i objektvariabler Det fins en spesiell konstruksjon som vi kaller Property som gjør det mulig å fortsatt å ha objektvariabler som er beskyttet inne i klassen, og samtidig tilby en enkel syntaks for å endre eller lese verdiene. En Property er en slags metode, men en metode med en veldig spesiell syntaks. Den brukes i de fleste objektorienterte programmeringsspråk. Strukturen ser slik ut og skrives inn i klassen: Public Class Person Private navn As String 'objektvariablene er kun tilgjengelige Private vekt As Double 'internt i denne klassen Private hoyde As Double 'siden de er deklarert som Private Public Property Vekten() As Double Get 'Kode du måtte ønske her, typisk Return vekt End Get Set(ByVal value As Double) 'Kode du måtte ønske her, typisk vekt = value End Set
Objektorientering i VB videregående side 15 av 19 End Property 'Eventuelt flere Property-strukturer og metoder her... '... '... End Class En Property kan brukes i stedet for metoder som for eksempel lagrehoyde() og henthoyde(). Det er nødvendig å se på bruken av Property-en før vi kan forklare forskjellen på Get og Set. Gitt følgende kode i Button1_Click: 'Demonstrerer bruk av Property Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click 'Lager et objekt av type Person Dim jens As Person jens = New Person 'Registrerer en verdi i objektvariabelen vekt vha Property Vekten jens.vekten = 97 'Skriver ut verdien i objektvariabelen vekt vha Property Vekten MsgBox(jens.Vekten) 10.4.2. Forklaring til Get og Set Det som står mellom Get og End Get aktiveres ganske enkelt når vi i knappen Button1_Click (eller liknende) skriver MsgBox(jens.Vekten) Når vi skriver på syntaksen objekt.propertynavn, så ønsker vi typisk å hente ut verdien. Siden Property-strukturen Vekten() egentlig er en slags metode, kan vi bruke kodeordet Return for å returnere en verdi. Det kan være forvirrende at Property-en ikke skrives på samme måte som vanlige funksjoner og metoder når den kalles opp, men det er noe du bare må akseptere. Det som nå er viktig å huske på, er at objektvariabelen vekt er deklarert som Private i klassen. Det betyr at vekt ikke kan hentes utenfor klassen, men den kan derimot hentes av alle metoder inne i klassen. Dette inkluderer vår Property som heter Vekten(). Dermed kan vi skrive setningen Return vekt for å sikre at når en i for eksempel Button1_Click skriver inn MsgBox(jens.Vekten), så vil kompilatoren aktivere objektet jens sin Property som heter Vekten() og returnere innholdet i objektvariabelen vekt. Dermed står det i praksis MsgBox(97), slik at 97 skrives ut. Det som står mellom Set og End Set aktiveres når vi skriver jens.vekten = 97 Tallet bak likhetstegnet sendes som argument til Set-delen av Property-en som heter Vekten(), og dette argumentet blir gitt navnet value i vårt tilfelle. Vi kunne kalt det noe annet (mer om det straks). Det vi typisk ønsker når vi skriver slik, er jo å endre objektvariabelen vekt, slik at den får verdien 97. Derfor er det fornuftig å ha koden vekt = value inne i Set-delen. Merk at vi kunne hatt hvilken som helst kode inne i Get-blokken og Set-blokken. Det er mest fornuftig å henholdsvis lese og sette (lagre) verdier i tilhørende objektvariabler, men det er
Objektorientering i VB videregående side 16 av 19 ikke noe krav om det. En kunne også gjort mye mer, for eksempel loggført det som skjedde til en tekstfil eller en database, en kunne foretatt feilsjekking (validering) og så videre. Som regel er det derimot nok å bare lese/sette objektvariablene, men husk for fremtiden at mer går an. 10.4.3. Detaljer og ytterligere forklaring Det er noen detaljer som er verdt å merke seg, som leder oss til en slags beste praksis ved bruk av Property i Visual Basic: Det er vanlig i VB å bruke stor bokstav når en navngir en Property. Merk at det skal være parentes etter selve navnet på en Property, for eksempel Vekten(). I klassen Person har vi en objektvariabel som heter vekt. Det er vel og bra, men siden vi ikke kan ha samme navngiving på to variabler, en funksjon og en variabel, en variabel og en konstant og så videre, så må vi kalle vår Property for noe annet enn Vekt(). Vi har valgt Vekten() som navn. Det er litt dumt. Det ville vært bedre å gjøre motsatt: å la objektvariabelen hete vekten, og så la egenskapen (Property-en) hete Vekt(). Da ville brukeren kunne skrive lise.vekt = 62, noe som er mer naturlig enn å skrive lise.vekten = 62. Det er ofte ønskelig å ha logiske navn på sine egenskaper/properties. En mye brukt konvensjon for å oppnå dette, er å la objektvariabler starte med prefikset m_, for eksempel: m_vekt, m_navn, m_hoyde. Bokstaven m står for member, fordi objektvariabel på engelsk gjerne kalles for member variable. Noen bruker o_ som prefiks, og tenker da på objektvariabel. Du velger selv om du i det hele tatt vil bruke prefiks eller ikke. Gjør du det, så kan du i hvertfall trygt bruke logiske navn på det du lager som Property-navn, og da blir det enklere å bruke objektet utenfor klassen. Det er viktig at selve Property-en er deklarert som Public. Modifikatoren angir hvor noe skal være synlig, og Public gjør det mulig for Property-en å brukes utenfor klassen. Det er som regel det vi ønsker. Vi bruker samme datatype i vår Property som på objektvariabelen som skal behandles. 10.4.4. Hva har en skarve TextBox og en Label å gjøre med en Ahaopplevelse? Den observante leser har allerede fått et deja vû, men har kanskje ikke lokalisert dette enda. Vi skal nå gjøre et VIKTIG EKSPERIMENT. Tenk først litt over måten å skrive disse setningene på: Dim kari as New Person Dim ut as String kari.vekten = 62 ut = "Kari veier " & kari.vekten & " kilo" MsgBox(ut) Sammenlikn så koden over, med følgende kode: Dim antallkg as Double
Objektorientering i VB videregående side 17 av 19 Label1.Text = 62 ut = "Kari veier " & Label1.Text & " kilo" MsgBox(ut) Denne koden kunne du ha laget nokså tidlig i din karriere som VB-programmerer. Ser du likheten? TextBox1.Text, Label1.Text og så videre har samme skrivemåte som vår bruk av Property. Det er fordi TextBox1 bare er et konkret objekt som er opprettet basert på klassen TextBox, og denne klassen har tilfeldigvis en Property som heter Text(). Derfor kan vi skrive slik.! Aha! Det er altså sånn det henger sammen! Alle objekter i grensesnittet er nettopp det: objekter, og da har de selvsagt metoder og Properties (gjerne kalt egenskaper på norsk). De løse trådene begynner nå å nøste seg sammen i synk med at vi ser nærmere på mekanismene bak objektorientering. Hvis dette var uklart, så dvel litt ved eksempelet og les det på nytt på et senere tidspunkt. Figur 11: Egenskapene (Properties) til en bestemt Label til venstre og en TextBox til høyre. Utviklingsmiljøet lar oss på en enkel måte lese verdiene og endre disse. Legg merke til hvordan en Label kan ha mange egenskaper som kan endres (se Figur 11) i utviklingsmiljøet. Egenskapene kontrollerer hvordan objektet oppfører seg. Det fine er at en også kan endre disse egenskapene i koden. Det er faktisk det som skjer bak kulissene når du endrer noe i utviklingsmiljøet (ala Figur 11)! Dette blir kanskje Aha-opplevelse nummer to for noen. Utviklingsmiljøet er altså bare et grensesnitt inn mot koden, og det er laget for at vi på en enklere, mer visuell måte, skal kunne fylle objektene våre med innhold! Dersom du i koden vil gjøre TextBox1 usynlig, er det bare å skrive: TextBox1.Visible = False Det som da egentlig skjer, er at Property-en Visible() sin Set-del i klassen som definerer tekstbokser, utføres. Du kan tilsvarende teste på om en tekstboks er synlig ved å skrive for eksempel: If TextBox1.Visible Then MsgBox("Du kan nå skrive inn noe i tekstboksen, kjære bruker") Else MsgBox("Boksen er usynlig, din uheldiggris") End If
Objektorientering i VB videregående side 18 av 19 Nå er det Get-delen som aktiveres. Den returnerer enten True eller False, avhengig av om tekstboksen er synlig eller ikke. Hvis den returnerer True, så står det i praksis If True Then... og da vil meldingsboksen vises med beskjed om at brukeren kan skrive inn noe i tekstfeltet. Hvis den returnerer False, så står det i praksis If False Then... og da blir ikke brukeren like oppmuntret. 10.4.5. En siste Aha-opplevelse Vi gir oss ikke, nå skal vi helt til bunns i alle hemmeligheter. Enter Aha-experience number three : Når du lager et program, så starter du gjerne med en Form. Du legger så til en knapp, du drar litt i hjørnene for å få den større, du endrer kanskje farge og en rekke andre ting. Du lager så en listeboks, og er fornøyd. Det tar 30 sekunder. Resultatet vises i Figur 12. Figur 12: En form med en button og en listbox laget på 30 sekunder i utviklingsmiljøet... Du kunne også programmert alt dette, for hånd. Dersom du i Solution Explorer klikker på det andre ikonet, så får du se noen skjulte filer. Der finner du blant annet all den koden som representerer Form1. Ved å se på koden i filen Form1.Designer.vb, ser vi følgende auto-genererte kode: Me.Button1 = New System.Windows.Forms.Button Me.ListBox1 = New System.Windows.Forms.ListBox Me.SuspendLayout() ' 'Button1 ' Me.Button1.Font = New System.Drawing.Font("Microsoft Sans Serif", 20.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte)) Me.Button1.ForeColor = System.Drawing.Color.RoyalBlue Me.Button1.Location = New System.Drawing.Point(22, 12) Me.Button1.Name = "Button1" Me.Button1.Size = New System.Drawing.Size(174, 116) Me.Button1.TabIndex = 0 Me.Button1.Text = "Min superknapp!" Me.Button1.UseVisualStyleBackColor = True ' 'ListBox1 ' Me.ListBox1.FormattingEnabled = True Me.ListBox1.Location = New System.Drawing.Point(230, 33)
Objektorientering i VB videregående side 19 av 19 Me.ListBox1.Name = "ListBox1" Me.ListBox1.Size = New System.Drawing.Size(120, 95) Me.ListBox1.TabIndex = 1 ' 'Form1 ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.ClientSize = New System.Drawing.Size(393, 166) Me.Controls.Add(Me.ListBox1) Me.Controls.Add(Me.Button1) Me.Name = "Form1" Me.Text = "Form1" Me.ResumeLayout(False) Dette er altså koden som skal til for å lage grensesnittet i Figur 12! Aha, aha, aha! I koden ser du noen setninger og uttrykk i fet skrift. Det er for å tydeliggjøre at det som er formattert i grensesnittet, i bunn og grunn representeres med kode. Listeboksen er dradd noe til høyre på Form1, og den har en viss størrelse. Dette er kodet vha egenskaper som.location og.size. Vi ser at først opprettes en knapp ved hjelp av koden Me.Button1 = New System.Windows.Forms.Button Knappen får en skrifttype, nemlig Microsoft Sans Serif på 20 punkter, og fargen settes til blå: Me.Button1.Font = New System.Drawing.Font("Microsoft Sans Serif", 20.0! '... Me.Button1.ForeColor = System.Drawing.Color.RoyalBlue Knappen får et navn, og en tekst som vises for brukeren: Me.Button1.Name = "Button1" Me.Button1.Text = "Min superknapp!" Tenk nå over hvor tidkrevende det ville vært å skrive denne koden selv. Du kan selvsagt gjøre det, hvis du vil, og du kan skrive koden i Notisblokk, hvis du vil, men det tar garantert ikke 30 sekunder. Aha.