Am discutat despre programarea orientată pe obiecte pentru dezvoltatorii de jocuri în general și despre principiile OOP specifice de coeziune și de cuplare. Acum, să aruncăm o privire la încapsulare și cum ajută la menținerea codului liber cuplat și mai ușor de întreținut.
Notă: Deși acest sfat rapid este explicat folosind Java, ar trebui să puteți utiliza aceleași tehnici și concepte în aproape orice mediu de dezvoltare a jocului.
Encapsularea este principiul ascunderii informațiilor. Aceasta este punerea în aplicare (funcționarea internă) a unui obiect este ascunsă de restul programului.
Un exemplu popular pe care îl veți auzi pentru încapsulare este că conduceți o mașină. Trebuie să știți exact cum funcționează fiecare aspect al unei mașini (motor, carburator, alternator și așa mai departe)? Nu - trebuie să știți cum să utilizați volanul, frânele, acceleratorul și așa mai departe.
Un alt exemplu este căutarea unei valori într-o matrice. În Java, puteți face următoarele:
int myArray [] = 1, 2, 3, 5, 7, 9, 11, 13; Arrays.asList (myArray) .contains (11);
Codul de mai sus va reveni Adevărat
dacă valoarea 11
este in myArray
, altfel se va întoarce fals
. Cum se face conține ()
metoda de lucru? Ce tehnică de căutare utilizează? Pre-sortează matricele înainte de a căuta? Raspunsul este nu contează deoarece implementarea exactă a metodei este ascunsă.
Encapsularea contribuie la crearea unui cod care este cuplat slab. Deoarece detaliile sunt ascunse, aceasta reduce capacitatea altor obiecte de a modifica direct starea și comportamentul unui obiect.
De asemenea, vă ajută foarte mult când trebuie să modificați tipul de date al unei variabile. Să spunem că ați decis să utilizați a Şir
pentru a urmări timpul în format "hh: mm: ss". După un timp, ați ajuns să realizați acest lucru int
reprezentând secunde poate fi un tip de date mai bun pentru timp. Nu numai că trebuie să modificați tipul de date în obiect, dar și de fiecare dată când ați referit timpul obiectului în întregul program!
În schimb, puteți utiliza ceea ce se numește funcții getter și setter. Getters și setters sunt de obicei mici funcții care se întorc și fixează o variabilă respectiv. O funcție getter pentru a obține timpul ar arăta după cum urmează:
public String getTime () timp de întoarcere;
Receptorul va returna a Şir
valoare: variabila timp
. Acum, când vrem să ne schimbăm timp
la un int, în loc de a schimba toate apelurile către getter, putem schimba funcția getter pentru a schimba int
tipul de date într-un Şir
tip de date.
public String getTime () // împărți timpul în ore, minute și secunde int ore = timp / 3600; int remainder = time% 3600; int minute = restul / 60; int secunde = restul% 60; // // combinați rezultatul într-un ore de întoarcere a șirului separat de colon + ":" + minute + ":" + secunde;
%
operatorul este cunoscut ca operatorul modulului și returnează restul unei diviziuni. Asa de 10% 3
s-ar întoarce 1
de cand 10/3 = 3
cu un rest de 1
. Funcția getter revine acum a Şir
și niciuna dintre apelurile la funcție nu trebuie să se schimbe. Prin urmare, codul este mai ușor de întreținut.
Să ne întoarcem la exemplul unei nave care arde un glonț introdus în articolul de cuplare. Reamintim că am definit o navă după cum urmează:
/ ** * Clasa de navă * / Nava de clasă publică / ** * Funcție - efectuează comportamentul (sarcina) de a schimba nava * / void rotate () // Cod care transformă nava / ** * - efectuează comportamentul (sarcina) de mutare a navei * / mișcarea publică voidă () // Cod care mută nava / ** * Funcție - efectuează comportamentul (sarcina) de a trage arma navei * ) // Codul care face ca nava să tragă un glont
Pentru ca nava să tragă un glonț, tot ce trebuie să faceți este să sunați ship.fire ()
. Modul în care codul implementează aruncarea unui glonț nu este important, deoarece tot ce ne pasă este arderea unui glonț. În acest fel, dacă doriți să modificați codul pentru a declanșa o explozie cu laser, trebuie doar să schimbați metoda foc()
și nu fiecare apel la ship.fire ()
.
Acest lucru vă permite să aveți capacitatea de a schimba orice aspect al funcționării navei, fără a fi nevoie să schimbați modul în care obiectul navei este utilizat oriunde în altă parte.
Tetris are câteva moduri diferite de a face față liniilor de compensare. Încapsularea comportamentului de compensare a unei linii vă va permite să schimbați repede metoda pe care o utilizați fără a trebui să rescrieți fiecare utilizare a acesteia. Acest lucru vă va permite chiar să modificați metoda liniei clare pentru a crea diferite moduri de joc în joc.
Există încă o caracteristică a încapsulării care se referă la ascunderea accesului la un obiect. Există de multe ori în care nu doriți acces extern la starea sau comportamentul unui obiect și doriți să îl ascundeți de orice alt obiect. Pentru a face acest lucru, puteți utiliza modificatori de nivel de acces care dau sau ascund accesul la stările și comportamentele obiectelor.
Modificatorii de nivel de acces definesc două niveluri de acces:
public
- orice obiect poate accesa în orice moment variabila sau funcția.privat
- numai obiectul care conține variabila sau funcția îl poate accesa.(Există un al treilea nivel - protejat
- dar vom acoperi acest lucru într-o postare viitoare.)
Reamintim că o fantomă a avut stări de:
culoare
Nume
stat
direcţie
viteză
... și a fost definită după cum urmează:
/ ** * Clasa Ghost * / Ghost clasa publica / ** * Functie - muta Ghost * / move void public () // Cod care muta Ghost in directia curenta / ** * Function - change Ghost direcția * / void public changeDirection () // Cod care schimbă direcția Ghost / ** * Funcție - schimbare Ghost speed * / public void changeSpeed () // Cod care schimbă viteza Ghost / ** * schimbare Ghost color * / public void changeColor () // Cod care schimbă culoarea Ghost / ** * Funcție - schimbare Ghost state * / public void changeState () // Cod care schimbă statutul Ghost // Această funcție, de asemenea, va apela cele trei funcții ale changeDirection (), changeSpeed () și changeColor ()
De cand schimba culoarea()
, changeSpeed ()
, și schimba directia()
sunt funcții de ajutor și nu se presupune că sunt accesate din orice altă parte decât din interiorul clasei, le putem defini ca fiind privat
funcții. Toate stările fantomului pot fi de asemenea declarate privat
deoarece acestea nu ar trebui să fie modificate din afara clasei fie, și accesate doar prin intermediul getter și setter funcții. Aceasta ar schimba clasa care urmează să fie definită după cum urmează:
/ ** * Clasa Ghost * / clasa publica Ghost // Ghost stres privat de culoare String; numele String privat; privat Boolean isEatable; direcția privată Vector; viteza privată int; / ** * Functia - schimbarea directiei Ghost * / void private changeDirection () // Codul care se deplaseaza in directia curenta / ** * schimbă direcția Duhului / ** * Funcție - schimbare Ghost speed * / void private changeSpeed () // Cod care schimbă viteza lui Ghost / ** * Funcție - schimbare Ghost color * / private void changeColor () // Codul care schimbă culoarea Duhului / ** * Funcție - schimbarea stării Ghost * / public void changeState () // Cod care schimbă starea Ghost // Această funcție va apela și cele trei funcții ale changeDirection, changeSpeed și changeColor / ** * Getters și setteri * / ...
Encapsularea poate ajuta la crearea unui cod mai sustenabil, ajutând la prevenirea efectului de ripple al schimbărilor de cod. De asemenea, vă ajută la crearea unui cod cuplat în mod liber prin reducerea accesului direct la starea și comportamentul unui obiect.
În următorul Sfat rapid, vom discuta principiul abstractizării și cum poate ajuta la reducerea redundanței codului. Urmăriți-ne pe Twitter, Facebook sau Google+ pentru a fi la curent cu cele mai recente postări.