Java. Hermetyzacja
Java. Hermetyzacja
Hermetyzacja w Javie i programowaniu obiektowym
Java programowanie obiektowe - strona g��wna Programowanie sterowane zdarzeniami Hermetyzacja Dziedziczenie Polimorfizm Inne publikacje Analiza portfelowa Projektowanie magazynów Stylystycznie blog Moda męska

Hermetyzacja

Hermetyzacja to sposób odizolowania od otoczenia wybranych danych i funkcji (operujących na tych danych) zgromadzonych w jednej strukturze. Widoczne są tylko niezbędne fragmenty programu, natomiast zmienne i funkcje pomocnicze są ukryte i niedostępne z zewnątrz. Dzięki takiemu połączeniu programista uwalnia się od pamiętania o wszystkich szczegółach implementacyjnych, co zapewnia zmniejszenie liczby błędów oraz prostszą strukturę programu końcowego. Ukrywanie szczegółów zawiera moja książka w jednym z podrozdziałów Dla terminu hermetyzacja spotyka się również określenie enkapsulacja. Podobnie jak w innych językach programowania, hermetyzację można stosować bez klasycznych mechanizmow programowani obiektowego.





Książka: Hermetyzacja. Ukrywanie szczegółów.

Marek Wierzbicki

Rozdział 2

...

2.1.4. Hermetyzacja i modyfikator private

Wprowadzając ideę programowania obiektowego (co zawiera wcześniej ta książka), zwracałem uwagę na jej podstawową cechę (i zarazem bardzo ważną zaletę), czyli hermetyzację (enkapsulację). Klasa (a wraz z nią obiekt) miała gromadzić w jednym miejscu dane i procedury ich obsługi. Jednak miało to być zgromadzone w taki sposób, aby osoba używająca obiektu miała jak najmniejszy dostęp do danych (tylko do tych niezbędnych). Miało to zapewnić zarówno zmniejszenie liczby błędów popełnianych w czasie kodowania, jak i podniesienie przejrzystości programu. Przedstawiona wcześniej klasa Point nie stanowiła idealnej reprezentacji hermetycznej klasy, gdyż udostępniała na zewnątrz wszystkie, a nie tylko niezbędne elementy. Aby uniemożliwić dostęp do pól, które w idei klasy nie muszą być dostępne z zewnątrz, należy je oznaczyć modyfikatorem private. Na listingu 2.5 przedstawiam poprawioną, bardziej hermetyczną wersję klasy Point.

Listing 2.5. Poprawiona klasa opisująca punkt

class Point {
  private int x;  // położenie na osi 0X
  private int y;  // położenie na osi 0Y

  // odczyt wartości
  public int getX() {
    return x;
  }
  public int getY() {
    return y;
  }
  // ustawienie nowej pozycji
  public void newPosition(int newX, int newY) {
    x = newX;
    y = newY;
  }
  // przemieszczenie punktu
  public void changePosition(int dX, int dY) {
    x = x+dX;
    y = y+dY;
  }
}

Na listingu 2.5 wytłuściłem różnice w stosunku do wcześniejszej wersji klasy, czyli ukrycie bezpośrednich wartości x i y oraz udostępnienie w zamian ich wartości przez metody getX i getY. Zaleta takiego rozwiązania jest oczywista: Nie można, nawet przez przypadek, odwołać się bezpośrednio do x i y, dzięki czemu nie może nastąpić przypadkowa ich modyfikacja. Aby je odczytać, trzeba jawnie wywołać getX lub getY. Aby je ustawić, trzeba jawnie wywołać newPosition (można też utworzyć metody setX i setY, aby ustawiać te parametry pojedynczo). Dopiero tak skonstruowana klasa spełnia warunki hermetyzacji.


...

2.5.1. Klasyczne użycie klasy anonimowej

Klasa anonimowa to klasa definiowana w miejscu, w którym tworzymy jej egzemplarz. Podobnie jak w przypadku klasycznych obiektów możemy je tworzyć w sposób jawny, tak aby następnie były dostępne przez nazwę, bądź niejawny, na przykład w czasie przekazywania ich do dalszego użycia wewnątrz innych klas. Najczęściej wykorzystuje się tę drugą metodę, gdyż w naturalny sposób łączy się ona z cechami klasy anonimowej. Tworzona jest na jednorazowy użytek za pomocą jednostkowej definicji, która nigdy więcej w tej postaci nie będzie używana. Klasa anonimowa jest tworzona według szablonu pokazanego na listingu 2.73.

Listing 2.73. Schemat tworzenia klasy anonimowej

new
KlasaBadzInterfejsBazowy
([lista argumentów])
{
ciało klasy anonimowej
}

Jak widać, klasa anonimowa może zostać zdefiniowana wyłącznie w chwili tworzenia jej egzemplarza, po wystąpieniu operatora new. Po tym operatorze musi wystąpić definicja typu, po którym dziedziczy klasa anonimowa. Może to być nazwa klasy bazowej bądź interfejsu. Jeśli podajemy nazwę interfejsu, domyślnie oznacza to, że klasa anonimowa dziedziczy po klasie Object i implementuje wymieniony interfejs. Nie jest możliwa implementacja większej liczby interfejsów niż jeden ani dziedziczenie po klasie innej niż Object i jednoczesna implementacja interfejsu. Przykład klasycznej definicji klasy anonimowej pokazany jest na listingu 2.74.

Listing 2.74. Klasyczna definicja klasy anonimowej

import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class Applet2 extends Applet {
  Button b = new Button("przycisk");
  public void init() {
    b.addActionListener(
      new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          // obsługa zdarzenia e
        }
      }
    );
    add(b, null);
  }
}

Wytłuszczony fragment to definicja klasy anonimowej dziedziczącej (niejawnie) po klasie Object i implementującej interfejs ActionListener. Interfejs ten deklaruje istnienie metody actionPerformed, która jest zdefiniowana w pokazanej klasie anonimowej. Klasa ta będzie wykorzystana w obiekcie typu Button jako klasa nasłuchująca zdarzeń pochodzących od przyciśniętego przycisku ekranowego. W chwili pojawienia się tego zdarzenia wywoływana będzie metoda actionPerformed klasy podawanej w metodzie addActionListener. W innych popularnych językach obiektowych, takich jak Object Pascal czy C++, w takim wypadku przekazywalibyśmy adres procedury, która byłaby wywoływana we właściwym momencie. Java jest tak mocno obiektowa, że jest to niemożliwe. Zamiast tego przekazuje się adres obiektu, który w tym wypadku jest nienazwanego typu. W związku z tym, że takie działanie wykonywane jest jednorazowo dla przyspieszenia tworzenia kodu, stosuje się klasę anonimową. Konstrukcja taka nie wpływa na czytelność kodu, jednak przyjęła się wśród osób używających Javy. Więcej na temat użycia klasy anonimowej i przykłady eliminacji anonimowości zawiera ta książka w rozdziale 4. "Programowanie sterowane zdarzeniami" oraz w artykule "Java. Poprawianie obsługi zdarzeń po środowiskach RAD". Na listingu 2.75 zaprezentowałem jeszcze jeden przykład użycia klasy anonimowej. Pozornie nie wnosi on nic nowego poza zaciemnieniem kodu. W praktyce umożliwia wyodrębnienie i zgrupowanie kodu w jeden blok, podobnie jak podprogramy czy procedury lokalne w klasycznym programowaniu strukturalnym.

Listing 2.75. Użycie klasy anonimowej do zgrupowania kodu

(new Object() {
  // definicja pól
  public void make() {
    // działania w metodzie make

  }
}).make();

Przedstawiona klasa anonimowa gromadzi pewne instrukcje wewnątrz metody make, umożliwiając dostęp do lokalnych pól tej klasy. Stosowania takiej konstrukcji ma sens w przypadku, gdy chcemy stworzyć silniejszą hermetyzację wewnątrz pojedynczej metody.