Vi har diskutert objektorientert programmering for spillutviklere generelt og de spesifikke OOP-prinsippene for kohesjon og kopling. La oss ta en titt på innkapsling og hvordan det bidrar til å holde koden løst koblet og mer vedlikeholdsbar.
Merk: Selv om denne Quick Tip forklares med Java, bør du kunne bruke de samme teknikkene og konseptene i nesten hvilket som helst spillutviklingsmiljø.
Innkapsling er prinsippet om å skjule informasjon. Det er det gjennomføring (det interne arbeidet) til et objekt er skjult fra resten av programmet.
Et populært eksempel du hører for innkapsling kjører bil. Trenger du å vite nøyaktig hvordan alle aspekter av en bil fungerer (motor, karburator, generator osv.)? Nei - du må vite hvordan du bruker rattet, bremsene, akseleratoren osv.
Et annet eksempel er å søke etter en verdi i en matrise. I Java kan du gjøre følgende:
int myArray [] = 1, 2, 3, 5, 7, 9, 11, 13; Arrays.asList (myArray) .contains (11);
Ovennevnte kode vil returnere ekte
hvis verdien 11
er i myArray
, ellers kommer det tilbake falsk
. Hvordan gjør inneholder ()
metode arbeid? Hvilken søketeknikk bruker den? Pre-sorterer arrayet før du søker? Svaret er det spiller ingen rolle fordi den nøyaktige implementeringen av metoden er skjult.
Encapsulation bidrar til å lage kode som er løst koblet. Fordi detaljene er skjult, reduserer det muligheten til andre objekter til å direkte endre et objekts tilstand og oppførsel.
Det hjelper også sterkt når du må endre datatypen til en variabel. La oss si at du bestemte deg for å bruke a string
å holde oversikt over tid i "hh: mm: ss" format. Etter en stund kommer du til å innse at en int
som representerer sekunder kan være en bedre datatype for tiden. Ikke bare må du endre datatypen i objektet, men også hver gang du refererte objektets tid i hele programmet!
I stedet kan du bruke det som kalles getter og setterfunksjoner. Getters og setters er vanligvis små funksjoner som returnerer og angir en variabel henholdsvis. En getter-funksjon for å få tiden, ville se ut som følger:
offentlig String getTime () returtid;
Den getter vil returnere a string
verdi: variabelen tid
. Nå når vi ønsker å forandre seg tid
til en int, i stedet for å endre alle anrop til getteren, kan vi bare endre getter-funksjonen for å endre int
datatype til a string
data-type.
offentlig String getTime () // splitt tiden opp i timer, minutter og sekunder int timer = tid / 3600; int resten = tid% 3600; int minutter = rest / 60; int sekunder = rest% 60; // / kombinere resultatet til en kolon-separert streng retur tid + ":" + minutter + ":" + sekunder;De
%
Operatør er kjent som moduloperatøren og returnerer resten av en divisjon. Så 10% 3
ville returnere 1
siden 10/3 = 3
med en rest av 1
. Getter-funksjonen returnerer nå a string
og ingen av anropene til funksjonen må endres. Koden er derfor mer vedlikeholdsbar.
La oss gå tilbake til eksempelet på et skip som skyter en kule inn i koblingsartikkelen. Husk at vi definerte et skip som følger:
/ ** * Skipsklassen * / offentlig klasse Skip / ** * Funksjon - utfører oppførselen (oppgave) for å snu skipet * / offentlig tomgang rotere () // Kode som gjør skipet / ** * Funksjon - utfører oppførselen (oppgave) for å flytte skipet * / offentlig tomgangshastighet () // kode som beveger skipet / ** * funksjon - utfører oppførselen (oppgave) for å skyte skipets pistol * / offentlig tomrom ) // Kode som gjør skipet brann en kule
For å få skipet til å brenne en kule, er alt du trenger å gjøre, å ringe ship.fire ()
. Hvordan koden implementerer å skyte en kule er ikke viktig, fordi alt vi bryr oss om, skyter en kule. På denne måten, hvis du vil endre koden for å brenne en laserblast i stedet, må du bare endre metoden Brann()
og ikke alle anrop til ship.fire ()
.
Dette gjør det mulig for deg å endre evnen til å se hvordan skipet fungerer uten å måtte endre hvordan skipsobjektet brukes overalt.
Tetris har noen forskjellige måter å håndtere rydde linjer. Innkapsling av oppførselen ved å rydde en linje vil tillate deg å raskt endre hvilken metode du bruker uten å måtte omskrive hver bruk av den. Dette vil til og med tillate deg å kunne endre klare linjemetoden for å skape forskjellige spillmoduser i spillet.
Det er en ekstra funksjon av innkapsling som omhandler å gjemme tilgangen til en gjenstand. Det er mange ganger hvor du ikke vil ha ekstern tilgang til en objekts tilstand eller oppførsel, og vil så gjemme det fra noe annet objekt. For å gjøre dette kan du bruke tilgangsnivåmodifikatorer som gir eller gjemmer tilgang til objekteres tilstander og atferd.
Tilgangsnivåmodifikatorer definerer to tilgangsnivåer:
offentlig
- Ethvert objekt kan når som helst få tilgang til variabelen eller funksjonen.privat
- Bare objektet som inneholder variabelen eller funksjonen, kan få tilgang til det.(Det er et tredje nivå - beskyttet
- men vi vil dekke det i et fremtidig innlegg.)
Husk at et spøkelse hadde tilstander om:
farge
Navn
stat
retning
hastighet
... og ble definert som følger:
/ ** * Spøkelsesklassen * / offentlig klasse Ghost / ** * Funksjon - flytter Ghost * / public void move () // Kode som beveger Ghost i den nåværende retningen / ** * Funksjon - endre Ghost retning * / public void changeDirection () // Kode som endrer Ghosts retning / ** * Funksjon - endre Ghost speed * / public void changeSpeed () // Kode som endrer Ghosts hastighet / ** * Funksjon - endre Ghost farge * / offentlig ugyldig endringColor () // Kode som endrer Ghosts farge / ** * Funksjon - endre Ghost state * / public void changeState () // Kode som endrer Ghosts tilstand // Denne funksjonen også vil ringe de tre funksjonene til changeDirection (), changeSpeed () og changeColor ()
Siden changeColor ()
, changeSpeed ()
, og endre retning()
er hjelperfunksjoner og skal ikke nås fra andre steder, men inne i klassen kan vi definere dem som privat
funksjoner. Alle spøkelsens tilstander kan også deklareres privat
siden de ikke bør endres fra utenfor klassen heller, og bare åpnes gjennom getter og setterfunksjoner. Dette vil endre klassen som skal defineres som følger:
/ ** * The Ghost Class * / offentlig klasse Ghost // Ghost stater private String farge; privat strenge navn; privat boolsk isEatable; privat vektor retning; privat int hastighet; / ** * Funksjon - flytter Ghost * / public void move () // Kode som flytter spøkelset i den nåværende retningen / ** * Funksjon - endre Ghost direction * / private void changeDirection () // Kode som endrer spøkelsens retning / ** * Funksjon - endre Ghost Speed * / Private void changeSpeed () // Kode som endrer Ghosts fart / ** * Funksjon - endre Ghost color * / private void changeColor () // Kode som endrer Ghosts farge / ** * Funksjon - endre Ghost state * / public void changeState () // Kode som endrer Ghosts tilstand // Denne funksjonen vil også ringe de tre funksjonene til changeDirection, changeSpeed og changeColor / ** * Getters og setters * / ...
Encapsulation kan bidra til å skape mer vedlikeholdsbar kode ved å bidra til å forhindre rippleeffekten av kodeendringer. Det hjelper også med å lage løst koblet kode ved å redusere direkte tilgang til objektets tilstand og oppførsel.
I neste Quick Tip, diskuterer vi prinsippet om abstraksjon og hvordan det kan bidra til å redusere kode redundans. Følg oss på Twitter, Facebook eller Google+ for å holde deg oppdatert med de siste innleggene.