Cześć! Dzisiejszy wykład poświęcony będzie enkapsulacji i od razu zaczniemy od przykładów :) Przed Tobą znajomy automat z napojami gazowanymi. Mam do Was jedno pytanie: jak to działa? Spróbuj odpowiedzieć szczegółowo: skąd pochodzi szklanka, jak utrzymuje się temperatura w środku, gdzie przechowywany jest lód, jak maszyna rozumie, jaki syrop dodać itp. Najprawdopodobniej nie znasz odpowiedzi na te pytania. No cóż, może nie wszyscy korzystają z takich maszyn, obecnie nie są one już tak popularne. Spróbujmy podać inny przykład. Coś, o czym wiesz, że używasz wiele razy każdego dnia. O, mam pomysł! Opowiedz nam jak działa wyszukiwarka Google . Jak dokładnie wyszukuje informacje na podstawie wpisanych słów? Dlaczego te wyniki są na górze, a nie inne? Chociaż na co dzień korzystasz z Google, prawdopodobnie o tym nie wiesz. Ale to nie jest ważne. W końcu nie musisz tego wiedzieć. Zapytania możesz wprowadzać do wyszukiwarki, nie zastanawiając się, jak to dokładnie działa. Możesz kupić napój gazowany w automacie, nie wiedząc, jak to działa. Można prowadzić samochód, nie zagłębiając się w działanie silnika spalinowego i nie znając w ogóle fizyki, nawet na poziomie szkolnym. Wszystko to jest możliwe dzięki jednej z głównych zasad programowania obiektowego – enkapsulacji . Czytając różne artykuły na ten temat, prawdopodobnie natknąłeś się na fakt, że w programowaniu istnieją dwa wspólne pojęcia - enkapsulacja i ukrywanie . A przez słowo „hermetyzacja” autorzy mają na myśli to czy tamto (tak się składa). Podzielimy oba terminy, abyś miał pełne zrozumienie. Pierwotne znaczenie słowa „ enkapsulacja ” w programowaniu to połączenie danych i metod pracy z tymi danymi w jednym pakiecie („kapsułka”). W Javie klasa działa jak pakiet kapsułkowy . Klasa zawiera zarówno dane (pola klas), jak i metody pracy z tymi danymi. Wydaje ci się to oczywiste, ale w innych koncepcjach programistycznych wszystko działa inaczej. Na przykład w programowaniu funkcjonalnym dane są ściśle oddzielone od operacji na nich. W OOP (programowanie obiektowe) programy składają się z klas kapsułek, które są zarówno danymi, jak i funkcjami do pracy z nimi. Porozmawiajmy teraz o ukrywaniu się . Jak to się dzieje, że korzystamy z najróżniejszych skomplikowanych mechanizmów, nie rozumiejąc, jak one działają i na czym polega ich działanie? To proste: ich twórcy zapewnili prosty i przyjazny dla użytkownika interfejs. W maszynie do napojów interfejsem są przyciski na panelu. Naciśnięciem jednego przycisku wybieramy objętość szklanki. Naciskając drugi wybieramy syrop. Trzeci odpowiada za dodanie lodu. I to wszystko, co musisz zrobić. Nie ma znaczenia, jak dokładnie maszyna jest zaprojektowana wewnątrz. Najważniejsze, że jest zaprojektowany w taki sposób, że aby otrzymać napój gazowany, użytkownik musi nacisnąć trzy przyciski . Podobnie jest z samochodem. Nie ma znaczenia, co się w nim dzieje. Najważniejsze, że po naciśnięciu prawego pedału auto jedzie do przodu, a po naciśnięciu lewego pedału zwalnia . To jest właśnie istota ukrycia. Wszystkie „wnętrza” programu są ukryte przed użytkownikiem. Dla niego ta informacja jest zbędna i niepotrzebna. Użytkownik potrzebuje wyniku końcowego, a nie procesu wewnętrznego. Spójrzmy na klasę jako przykład
Auto
:
public class Auto {
public void gas() {
/*w aucie dzieją się skomplikowane rzeczy,
w wyniku których jedzie do przodu*/
}
public void brake() {
/*w aucie dzieją się skomplikowane rzeczy,
przez co samochód zwalnia*/
}
public static void main(String[] args) {
Auto auto = new Auto();
// Jak wszystko wygląda dla użytkownika
//nacisnąłem jeden pedał - poszło
auto.gas();
//nacisnąłem inny pedał - zahamowałem
auto.brake();
}
}
Tak wygląda ukrywanie implementacji w programie Java. Wszystko jest jak w prawdziwym życiu: użytkownik otrzymuje interfejs (metody). Jeśli potrzebuje samochodu w programie do wykonania akcji, wystarczy, że wywoła żądaną metodę. A to, co dzieje się w tych metodach, to niepotrzebne informacje, najważniejsze, że wszystko działa tak, jak powinno. Tutaj rozmawialiśmy o ukryciu implementacji . Poza tym Java ma również funkcję ukrywania danych . Pisaliśmy o tym w wykładzie o getterach i setterach , ale nie zaszkodzi przypomnieć. Przykładowo mamy klasę Cat
:
public class Cat {
public String name;
public int age;
public int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Miauczeć!");
}
}
Być może pamiętacie z ostatniego wykładu, jaki jest problem tych zajęć? Jeśli nie, pamiętajmy. Problem w tym, że jego dane (pola) są otwarte dla każdego, a inny programista może bez problemu stworzyć w programie bezimiennego kota o wadze 0 i wieku -1000 lat:
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";
cat.age = -1000;
cat.weight = 0;
}
W takiej sytuacji możesz dokładnie monitorować, czy któryś z Twoich kolegów nie tworzy obiektów o złym stanie, jednak znacznie lepiej byłoby wykluczyć samą możliwość powstania takich „złych obiektów”. Pomagają nam w ukrywaniu danych:
- modyfikatory dostępu ( prywatny , chroniony , domyślny pakiet );
- gettery i settery.
- Monitorowanie prawidłowego stanu obiektu. Były tego przykłady powyżej: dzięki setterowi i modyfikatorowi private zabezpieczyliśmy nasz program przed kotami o wadze 0.
- Przyjazny dla użytkownika dzięki interfejsowi. Pozostawiamy jedynie metody „na zewnątrz” umożliwiające dostęp użytkownika. Wystarczy, że do nich zadzwonisz i poznasz wynik, a on nie musi w ogóle zagłębiać się w szczegóły ich pracy.
- Zmiany w kodzie nie mają wpływu na użytkowników. Wszelkie zmiany wprowadzamy wewnątrz metod. Nie będzie to miało wpływu na użytkownika: napisał auto.gas() dla gazu w samochodzie, więc to napisze. A fakt, że zmieniliśmy coś w działaniu metody gas() pozostanie dla niego niewidoczny: on, tak jak poprzednio, po prostu otrzyma pożądany rezultat.
GO TO FULL VERSION