OBJEKTINIS PROGRAMAVIMAS
Java inkapsuliacija
Inkapsuliacija paslepia objekto vidinę būseną ir leidžia ją naudoti tik per aiškiai apibrėžtą viešą sąsają. Objektas pats kontroliuoja, kokie jo duomenų pakeitimai yra leidžiami.
Kodėl reikalinga inkapsuliacija?
- apsaugo objektą nuo netinkamos būsenos;
- paslepia vidines realizacijos detales;
- suteikia vieną vietą duomenims tikrinti;
- leidžia pakeisti vidinę realizaciją nekeičiant naudotojų kodo;
- aiškiai parodo, kokius veiksmus klasė leidžia atlikti.
Viešo lauko problema
Viešą atributą bet kuris kodas gali pakeisti be klasės kontrolės:
public class Knyga {
public int puslapiuSkaicius = 96;
public static void main(String[] args) {
Knyga knyga = new Knyga();
knyga.puslapiuSkaicius = -5;
System.out.println(knyga.puslapiuSkaicius);
}
}-5
- Atributas
puslapiuSkaiciusyrapublic, todėl bet kuris išorinis kodas gali jį pakeisti. - Pradinė tinkama reikšmė yra
96. - Išorinis kodas tiesiogiai įrašo
-5, nors knyga negali turėti neigiamo puslapių skaičiaus. - Klasė neturi galimybės patikrinti ar atmesti šio pakeitimo.
- Objektas lieka nelogiškos būsenos, o konsolėje matome -5.
Privati būsena ir kontroliuojama prieiga
Laukas slepiamas su private, o vieši metodai nustato skaitymo ir keitimo taisykles:
public class Knyga {
private int puslapiuSkaicius = 96;
public int getPuslapiuSkaicius() {
return puslapiuSkaicius;
}
public void setPuslapiuSkaicius(int naujasSkaicius) {
if (naujasSkaicius <= 0) {
System.out.println("Puslapių skaičius turi būti teigiamas");
return;
}
this.puslapiuSkaicius = naujasSkaicius;
}
public static void main(String[] args) {
Knyga knyga = new Knyga();
knyga.setPuslapiuSkaicius(-5);
System.out.println(knyga.getPuslapiuSkaicius());
}
}Puslapių skaičius turi būti teigiamas
96
- Dabar atributas yra
private, todėl išorinis kodas negali jo pakeisti tiesiogiai. - Reikšmė keičiama tik per setterį
setPuslapiuSkaicius(). - Setteris pirmiausia patikrina, ar perduotas skaičius didesnis už nulį.
- Reikšmė
-5netinkama, todėl parodomas pranešimas ir metodas baigiamas sureturn. - Objekto būsena nepasikeičia, tad getteris vis dar grąžina tinkamą reikšmę
96.
Inkapsuliacija nėra vien getteriai ir setteriai
Kartais geriau atverti prasmingą objekto veiksmą, o ne bendrą setterį. Likusių knygos puslapių skaičių saugiau mažinti metodu, kuris patikrina skaitymo veiksmą:
public class Knyga {
private int likoPuslapiu = 96;
public int getLikoPuslapiu() {
return likoPuslapiu;
}
public boolean skaityti(int puslapiai) {
if (puslapiai <= 0 || puslapiai > likoPuslapiu) {
return false;
}
likoPuslapiu -= puslapiai;
return true;
}
public static void main(String[] args) {
Knyga knyga = new Knyga();
System.out.println(knyga.skaityti(20));
System.out.println(knyga.getLikoPuslapiu());
System.out.println(knyga.skaityti(100));
System.out.println(knyga.getLikoPuslapiu());
}
}true
76
false
76
- Knygoje pradžioje likę
96puslapiai. skaityti(20)yra tinkamas veiksmas, todėl metodas atima 20, palieka76ir grąžinatrue.- Tada bandoma perskaityti
100puslapių, nors liko tik 76. - Metodas atmeta veiksmą, grąžina
falseir atributo nekeičia. - Todėl po abiejų bandymų likusių puslapių skaičius vis dar yra
76.
skaityti() išreiškia prasmingą objekto veiksmą.Bendras setteris leistų išoriniam kodui savavališkai nustatyti likusių puslapių skaičių ir apeiti skaitymo taisykles.
Nekintama ir tik skaitoma būsena
Jei reikšmė po objekto sukūrimo neturi keistis, galima naudoti private final lauką ir pateikti tik getterį:
public class Knyga {
private final String isbn;
public Knyga(String isbn) {
if (isbn == null || isbn.isBlank()) {
throw new IllegalArgumentException("ISBN privalomas");
}
this.isbn = isbn;
}
public String getIsbn() {
return isbn;
}
public static void main(String[] args) {
Knyga knyga = new Knyga("978-609-01-0000-0");
System.out.println(knyga.getIsbn());
}
}978-609-01-0000-0
- ISBN perduodamas konstruktoriui kuriant knygos objektą.
- Konstruktorius atmeta tuščią arba
nullreikšmę. - Patikrinta reikšmė išsaugoma privačiame ir nekintamame
finalatribute. - Viešas getteris leidžia ISBN perskaityti.
- Setterio nėra, todėl išorinis kodas negali paprašyti klasės ISBN pakeisti.
Atsargiai su keičiamais objektais
Vien private nepakanka, jei getteris grąžina keičiamo objekto nuorodą. Kolekcijai galima grąžinti nekintamą kopiją:
import java.util.ArrayList;
import java.util.List;
public class Knyga {
private final List pastabos = new ArrayList<>();
public void pridetiPastaba(String pastaba) {
pastabos.add(pastaba);
}
public List getPastabos() {
return List.copyOf(pastabos);
}
public static void main(String[] args) {
Knyga knyga = new Knyga();
knyga.pridetiPastaba("Perskaityti dar kartą");
List kopija = knyga.getPastabos();
System.out.println(kopija);
}
}[Perskaityti dar kartą]
- Vidinis pastabų sąrašas yra
private, todėl jį valdo patiKnygaklasė. - Pastaba pridedama per viešą metodą
pridetiPastaba(). - Getteris negrąžina tiesioginės vidinio sąrašo nuorodos.
List.copyOf(pastabos)sukuria nekintamą sąrašo kopiją.- Išorinis kodas gali perskaityti pastabas, bet negali per šią kopiją slapta pakeisti vidinio sąrašo.
Vieša sąsaja ir vidinė realizacija
| Dalis | Pavyzdžiai | Paskirtis |
|---|---|---|
| Vieša sąsaja | public metodai | Leidžiami veiksmai klasės naudotojams |
| Vidinė realizacija | private laukai ir pagalbiniai metodai | Duomenys ir detalės, kurias valdo pati klasė |
Praktinės gairės
- Laukus pagal nutylėjimą laikyk
private. - Viešink elgseną, o ne visas vidinės būsenos keitimo galimybes.
- Objekto taisykles tikrink konstruktoriuose ir būseną keičiančiuose metoduose.
- Nekurk setterio vien todėl, kad egzistuoja laukas.
- Negrąžink vidinių keičiamų kolekcijų tiesioginių nuorodų.

