We hebben objectgeoriënteerd programmeren besproken voor game-ontwikkelaars in het algemeen en de specifieke OOP-principes van cohesie en koppeling. Laten we nu eens kijken inkapseling en hoe het helpt om code losjes gekoppeld en meer onderhoudbaar te houden.
Notitie: Hoewel deze Quick Tip wordt uitgelegd met behulp van Java, zou je in bijna elke game-ontwikkelomgeving dezelfde technieken en concepten moeten kunnen gebruiken.
Inkapseling is het principe van informatie verbergen. Dat is de implementatie (de interne werking) van een object is verborgen voor de rest van het programma.
Een populair voorbeeld dat je zult horen voor inkapseling is het besturen van een auto. Moet u precies weten hoe elk aspect van een auto werkt (motor, carburateur, dynamo, enzovoort)? Nee - u moet weten hoe u het stuurwiel, de remmen, het gaspedaal enzovoort gebruikt.
Een ander voorbeeld is het zoeken naar een waarde in een array. In Java kunt u het volgende doen:
int myArray [] = 1, 2, 3, 5, 7, 9, 11, 13; Arrays.asList (myArray) .contains (11);
De bovenstaande code zal terugkeren waar
als de waarde 11
is in myArray
, anders zal het terugkeren vals
. Hoe doet de bevat ()
methode werk? Welke zoektechniek gebruikt het? Wordt de array vooraf gesorteerd voordat wordt gezocht? Het antwoord is het maakt niet uit omdat de exacte implementatie van de methode verborgen is.
Inkapseling helpt om code te creëren die losjes gekoppeld is. Omdat de details zijn verborgen, vermindert dit het vermogen van andere objecten om de staat en het gedrag van een object rechtstreeks te wijzigen.
Het helpt ook enorm als u het gegevenstype van een variabele moet wijzigen. Laten we zeggen dat je hebt besloten om een te gebruiken Draad
om de tijd bij te houden in het formaat "uu: mm: ss". Na een tijdje, kom je je realiseren dat een int
het weergeven van seconden kan een beter gegevenstype zijn voor de tijd. Niet alleen moet u het gegevenstype in het object wijzigen, maar ook telkens wanneer u naar de tijd van het object in het gehele programma verwijst!
In plaats daarvan kunt u de zogenaamde getter- en setterfuncties gebruiken. Getters en setters zijn meestal kleine functies die respectievelijk terugkeren en een variabele instellen. Een getterfunctie om de tijd te krijgen ziet er als volgt uit:
public String getTime () terugkomsttijd;
De vangstof zal terugkeren Draad
waarde: de variabele tijd
. Nu, wanneer we willen veranderen tijd
om een int in plaats van alle aanroepen van de getter te veranderen, kunnen we gewoon de getterfunctie veranderen om de te veranderen int
gegevenstype in a Draad
data type.
public String getTime () // deel de tijd op in uren, minuten en seconden int uren = tijd / 3600; int restder = tijd% 3600; int minuten = rest / 60; int seconden = rest% 60; // // combineer het resultaat in een door de colon gescheiden reeks retoururen + ":" + minuten + ":" + seconden;De
%
operator staat bekend als de modulus-operator en retourneert de rest van een divisie. Zo 10% 3
zou terugbrengen 1
sinds 10/3 = 3
met een rest van 1
. De getterfunctie retourneert nu een Draad
en geen van de oproepen naar de functie hoeft te veranderen. De code is daarom beter houdbaar.
Laten we teruggaan naar het voorbeeld van een schip dat een kogel afvuurt die is ingebracht in het artikel van de koppeling. Bedenk dat we een schip als volgt hebben gedefinieerd:
/ ** * De klasse Ship * / public class Ship / ** * Function - voert het gedrag (de taak) uit van het draaien van het schip * / public void rotate () // Code dat het schip verandert / ** * Functie - voert het gedrag (de taak) uit van het verplaatsen van de beweging Ship * / public void () // Code die het schip verplaatst / ** * Functie - voert het gedrag uit (taak) van het afvuren van het scheepsgeweer * / openbare leegte ( ) // Code waarmee het schip een kogel afvuurt
Om het schip een kogel te laten afvuren, hoef je alleen maar te bellen ship.fire ()
. Hoe de code implementeert bij het afvuren van een kogel, is niet belangrijk, want het enige waar we om geven is een kogel afvuren. Op deze manier, als je de code wilt veranderen om in plaats daarvan een laserstraal af te vuren, hoef je alleen maar de methode te veranderen brand()
en niet elke oproep aan ship.fire ()
.
Hiermee kunt u elk aspect van de werking van het schip wijzigen zonder dat u hoeft te veranderen hoe het scheepsobject overal elders wordt gebruikt.
Tetris heeft een paar verschillende manieren om met clearinglijnen om te gaan. Als u het gedrag van het wissen van een regel inkapselt, kunt u snel wijzigen welke methode u gebruikt, zonder dat u elk gebruik ervan hoeft te herschrijven. Hiermee kun je zelfs de methode met de onzichtbare lijn wijzigen om verschillende spelmodi te maken.
Er is nog een functie van inkapseling die zich bezighoudt met het verbergen van de toegang tot een object. Er zijn vaak situaties waarin u geen externe toegang tot de status of het gedrag van een object wilt hebben en u deze dus wilt verbergen voor een ander object. Hiertoe kunt u toegangsniveau-modifiers gebruiken die toegang geven tot de toestanden en het gedrag van objecten.
Modifiers voor toegangsniveaus definiëren twee toegangsniveaus:
openbaar
- elk object heeft op elk moment toegang tot de variabele of functie.privaat
- alleen het object dat de variabele of functie bevat, kan er toegang toe krijgen.(Er is een derde niveau - beschermde
- maar dat behandelen we in een volgend bericht.)
Bedenk dat een geest toestanden had van:
kleur
naam
staat
richting
snelheid
... en werd als volgt gedefinieerd:
/ ** * De Ghost Class * / public class Ghost / ** * Function - verplaatst de Ghost * / openbare ongeldige beweging () // Code die de Ghost in de huidige richting verplaatst / ** * Function - verander Ghost richting * / public void changeDirection () // Code die de richting van de Geest verandert / ** * Functie - verander Ghost-snelheid * / public void changeSpeed () // Code die de snelheid van de Geest verandert / ** * Functie - verander Spookkleur * / public void changeColor () // Code die de kleur van de Geest verandert / ** * Functie - verander Ghost state * / public void changeState () // Code die de status van de Geest verandert // Deze functie ook roept de drie functies van changeDirection (), changeSpeed () en changeColor ()
Sinds verander kleur()
, changeSpeed ()
, en verander richting()
zijn helper-functies en mogen niet van ergens anders dan binnen de klas worden benaderd, we kunnen ze definiëren als privaat
functies. Alle toestanden van de geest kunnen ook worden verklaard privaat
omdat ze ook niet van buiten de klas mogen worden gewijzigd en alleen toegankelijk zijn via de functies voor getter en setter. Dit zou de te definiëren klasse als volgt veranderen:
/ ** * De Ghost-klasse * / public class Ghost // Ghost-status private String-kleur; privé String naam; private Boolean isEatable; privé Vector richting; privé int snelheid; / ** * Functie - verplaatst de Ghost * / openbare ongeldige verplaatsing () // Code die de geest in de huidige richting verplaatst / ** * Functie - verander Spookrichting * / private ongeldige wijziging R & R () // Codeer dat verandert de richting van de Geest / ** * Functie - verander de snelheid van de Geest * / veranderlijke privé-snelheidSpeed () // Code die de snelheid van de Geest verandert / ** * Functie - verander de kleur van het Spook * / veranderlijke privé-veranderingKleur () // Code die de kleur van de Geest verandert / ** * Functie - verander Ghost-status * / public void changeState () // Code die de status van de Geest verandert // Deze functie zal ook de drie functies van changeDirection, changeSpeed en changeColor oproepen / ** * Getters en setters * / ...
Inkapseling kan helpen om meer onderhoudbare code te creëren door te helpen het rimpeleffect van codewijzigingen te voorkomen. Het helpt ook bij het maken van losjes gekoppelde code door directe toegang tot de staat en het gedrag van een object te verminderen.
In de volgende Quick Tip bespreken we het principe van abstractie en hoe het kan helpen om code-redundantie te verminderen. Volg ons op Twitter, Facebook of Google+ om op de hoogte te blijven van de laatste berichten.