Hermetyzacja (informatyka): Różnice pomiędzy wersjami

Z Wikipedii, wolnej encyklopedii
[wersja przejrzana][wersja nieprzejrzana]
Usunięta treść Dodana treść
literówka
ko
Linia 1: Linia 1:
'''Hermetyzacja''' (inna używana nazwa to enkapsulacja<ref>{{cytuj książkę|nazwisko=Grębosz|imię=Jerzy|tytuł=Symfonia C++ Standard|wydawca=Edition 2000|miejsce=Kraków|data=2005|strony=416|isbn=83-7366-073-9}}</ref>, [[język angielski|ang.]] ''encapsulation'') – jedno z założeń [[Programowanie obiektowe|programowania obiektowego]]. Hermetyzacja polega na ukrywaniu pewnych danych składowych lub [[metoda (programowanie obiektowe)|metod]] obiektów danej [[Klasa_(programowanie_obiektowe)|klasy]] tak, aby były one dostępne tylko metodom wewnętrznym danej klasy lub [[funkcja zaprzyjaźniona|funkcjom zaprzyjaźnionym]].
'''Hermetyzacja''' (inna używana nazwa to enkapsulacja<ref>{{cytuj książkę|nazwisko=Grębosz|imię=Jerzy|tytuł=Symfonia C++ Standard|wydawca=Edition 2000|miejsce=Kraków|data=2005|strony=416|isbn=83-7366-073-9}}</ref>, [[język angielski|ang.]] ''encapsulation'') – jedno z założeń [[Programowanie obiektowe|programowania obiektowego]]. Hermetyzacja polega na ukrywaniu pewnych danych składowych lub [[metoda (programowanie obiektowe)|metod]] obiektów danej [[Klasa_(programowanie_obiektowe)|klasy]] tak, aby były one dostępne tylko metodom wewnętrznym danej klasy lub [[funkcja zaprzyjaźniona|funkcjom zaprzyjaźnionym]].


Gdy dostępchuj
Gdy dostęp do wszystkich pól danej klasy jest możliwy wyłącznie poprzez metody, lub inaczej mówiąc: gdy wszystkie pola w klasie znajdują się w sekcji prywatnej lub chronionej, to taką hermetyzację nazywa się ''hermetyzacją pełną''.
do wszystkich pól danej klasy jest możliwy wyłącznie poprzez metody, lub inaczej mówiąc: gdy wszystkie pola w klasie znajdują się w sekcji prywatnej lub chronionej, to taką hermetyzację nazywa się ''hermetyzacją pełną''.


== Przyczyny stosowania hermetyzacji ==
== Przyczyny stosowania hermetyzacji ==

Wersja z 15:54, 15 mar 2016

Hermetyzacja (inna używana nazwa to enkapsulacja[1], ang. encapsulation) – jedno z założeń programowania obiektowego. Hermetyzacja polega na ukrywaniu pewnych danych składowych lub metod obiektów danej klasy tak, aby były one dostępne tylko metodom wewnętrznym danej klasy lub funkcjom zaprzyjaźnionym.

Gdy dostępchuj

do wszystkich pól danej klasy jest możliwy wyłącznie poprzez metody, lub inaczej mówiąc: gdy wszystkie pola w klasie znajdują się w sekcji prywatnej lub chronionej, to taką hermetyzację nazywa się hermetyzacją pełną.

Przyczyny stosowania hermetyzacji

Można wyróżnić trzy główne powody wprowadzenia hermetyzacji do programowania obiektowego:

  1. wyodrębnia interfejs
  2. uodparnia tworzony model na błędy,
  3. lepiej oddaje rzeczywistość,

Wyodrębnia interfejs

Cel

Głównym zadaniem wyodrębnienia interfejsu, a tym samym enkapsulacji, jest ukrycie przed użytkownikiem sposobu w jaki klasa wewnętrznie realizuje swoje zadanie. Metody i pola znajdujące się w sekcji publicznej stanowią interfejs, tj. jedyny dopuszczalny zbiór elementów klasy, którymi inne klasy mogą oddziaływać z daną klasą. Posiadanie wyodrębnionego interfejsu powoduje, że użytkownik danej klasy ma pewność, że korzystając z tych metod jest bezpieczny, tj. nie dojdzie do sytuacji w której klasa zostanie uszkodzona (np. zwolnienie pamięci pod wewnętrznym wskaźnikiem klasy). Metody interfejsowe, tj. ujawnione użytkownikowi klasy, w założeniu są absolutnie bezpieczne i korzystając tylko z nich nie można doprowadzić do nieprawidłowego stanu klasy.

Przykład

Posiadając zdefiniowany interfejs programista klasy ma pełną swobodę implementacji mechanizmu, w jaki klasa będzie wykonywała swoje zdanie. Jeżeli klasa ma za zadanie np. gromadzić zbiór liczb podawanych do niej pojedynczo a następnie, w dowolnym momencie, zwrócić maksimum tej kolekcji, to interfejs w żaden sposób nie narzuca programiście klasy konkretnego sposobu wykonania tej implementacji. Na przykładzie języka C++, klasa może to wewnętrznie wykonywać w oparciu o składową typu std::set jak również std::vector, który każdorazowo przed zwróceniem wartości maksymalnej będzie poddawany sortowaniu. Od strony użytkownika klasy sposób implementacji nie powinien być szczególnie interesujący, a co ważniejsze, nie powinien wpływać na sposób używania klasy.

Niech dana będzie klasa interfejsowa definiująca jedynie interfejs, czyli zbiór jedynie publicznych, abstrakcyjnych i wirtualnych metod. Metody te, zgodnie z definicją metody abstrakcyjnej, nie są wytłumaczone w obrębie tej klasy, a jedynie określono, że klasa dziedzicząca po tej klasie wytłumaczyć jak każda z tych metod działa. Na przykładzie:

class Sortowacz 
{
public: 
  virtual void dodajLiczbe(int liczba) = 0;
  virtual int maksimum() const = 0;
};

A następnie zakładając, że mamy dwie realne implementacje SortowaczNaWektorze oraz SortowaczNaZbiorze można wykonać następujący kod:

int main()
{
    const int tablica_rozmiar = 11;
    const int[] tablica = {1,3,4,0,5,2,6,7,1,3,4};
    
    Sortowacz* sort1 = new SortowaczNaZbiorze;
    Sortowacz* sort2 = new SortowaczNaWektorze;
    
    int indeks = 0;
    while ( indeks < tablica_rozmiar )
    {
        sort1.dodajLiczbe( tablica[indeks] );
        sort2.dodajLiczbe( tablica[indeks] );
        std::cout << "Sort1: Obecne maksimum to " << sort1.maksimum() << std::endl;
        std::cout << "Sort2: Obecne maksimum to " << sort2.maksimum() << std::endl;
        indeks++;
    }
    
    delete sort1;
    delete sort2;
    
    return 0;
}

Korzyść z takiego podejścia polega na tym, że interfejs dzięki enkapsulacji całkowicie milczy o sposobie zaimplementowania klasy a dodatkowo korzystając z mechanizmu polimorfizmu można posiadać wiele wymiennych klas realizujących ten sam interfejs, a to która z nich będzie wykorzystana może być zmieniane choćby w trakcie działania programu.

Wyodrębnianie interfejsu w językach bez wsparcia enkapsulacji

W językach w których nie istnieje mechanizm enkapsulacji (np. Python, C) stosuje się wyznaczanie interfejsu poprzez konwencję nazewnictwa, tj. składowe prywatne lub chronione mają specjalny przedrostek nazwy, np. ich nazwy zaczynają się od podkreślnika. Używanie metod wewnętrznych wiąże się z ryzykiem wywołania nieprawidłowego stanu programu co może prowadzić do awarii.

Uodparnia tworzony model na błędy

Hermetyzacja uodparnia tworzony model na błędy polegające np. na błędnym przypisywaniu wartości oraz umożliwia wykonanie czynności pomocniczych (jak np. pobranie z konta 10% wypłacanej kwoty jako prowizji) lub obsługę sytuacji wyjątkowej (np. brak wymaganych środków).

Przykład w C++:

typedef double TypPieniedzy;

class KontoBankowe {
public:
    KontoBankowe( const TypPieniedzy saldoPoczatkowe = 0 );

    bool wplac( const TypPieniedzy kwota );
    bool wyplac( const TypPieniedzy kwota );

    TypPieniedzy podajStanKonta() const;
private:
    TypPieniedzy saldo;
};

KontoBankowe::KontoBankowe( const TypPieniedzy saldoPoczatkowe ) :
    saldo( saldoPoczatkowe )
{}

bool KontoBankowe::wplac( const TypPieniedzy kwota ) {
    if ( kwota > 0 ) {
        saldo += kwota;
        return true;
    }
    return false;
}
bool KontoBankowe::wyplac( const TypPieniedzy kwota ) {
    // Powiększenie kwoty o 10% prowizji.
    TypPieniedzy kwotaProw = kwota*1.1;
    if ( ( kwotaProw > 0 ) && ( kwotaProw <= saldo ) ) {
        saldo -= kwotaProw;
        return true;
    }
    return false;
}

TypPieniedzy KontoBankowe::podajStanKonta() const {
    return saldo;
}

Przykład w Javie:

 class TypPieniedzy extends Double {
    public TypPieniedzy() {
        super();
    }
};
 
class KontoBankowe {
    private TypPieniedzy saldo;

    public KontoBankowe(TypPieniedzy saldoPoczatkowe) {
        saldo = saldoPoczatkowe;
    };

    public KontoBankowe() {
        KontoBankowe(0);
    };

    public boolean wplac( TypPieniedzy kwota ) {
        if ( kwota > 0 ) {
            saldo += kwota;
            return true;
        }
        return false;
    }
   
    public boolean wyplac( TypPieniedzy kwota ) {
        // Powiększenie kwoty o 10% prowizji.
        TypPieniedzy kwotaProw = kwota*1.1;
        if ( ( kwotaProw > 0 ) && ( kwotaProw <= saldo ) ) {
            saldo -= kwotaProw;
            return true;
        }
        return false;
    }

    public TypPieniedzy podajStanKonta() {
    	return saldo;
    }
 };

Mamy klasę KontoBankowe. Nie powinno się tak zdarzyć, że stan konta mógłby być modyfikowany przez zwykłe odwołanie się do danej saldo (np.: mojeKonto.saldo = 123;). Tu saldo konta bankowego jest daną prywatną (dostęp jest private), do której dostęp mają tylko funkcje zaprzyjaźnione lub funkcje składowe (wewnętrzne) (tu: podajStanKonta, wplac i wyplac). Powinno się zapewnić maksymalne bezpieczeństwo w odniesieniu do danej saldo. Stąd też metoda TypPieniedzy podajStanKonta() jest oznaczona jako const, wartość kwoty w metodach bool wplac( const TypPieniedzy kwota ) i bool wyplac( const TypPieniedzy kwota ) jest określona również jako const aby jej wartość nie została „przypadkiem” zmieniona w trakcie działania tych metod.

Hermetyzacja ma też na celu sprawdzanie poprawności wpisywanych danych, np. czy pozwolić użytkownikowi wpłacić na konto kwotę mającą wartość ujemną?

Lepiej oddaje rzeczywistość

Przykład powyższy obrazuje wykonywane na koncie bankowym operacje atomowe identyfikowane nazwami odpowiednimi do wykonywanej operacji, podczas gdy samo odwoływanie się w sposób bezpośredni do salda jako pewnej zmiennej nie dawałoby odwzorowania rzeczywistości reprezentowanej przez tę zmienną.

  1. Jerzy Grębosz: Symfonia C++ Standard. Kraków: Edition 2000, 2005, s. 416. ISBN 83-7366-073-9.