Hermetyzacja (programowanie komputerowe)
W systemach oprogramowania enkapsulacja odnosi się do łączenia danych z mechanizmami lub metodami, które działają na danych. Może również odnosić się do ograniczenia bezpośredniego dostępu do niektórych z tych danych, takich jak elementy składowe obiektu. Enkapsulacja umożliwia programistom przedstawienie spójnego i użytecznego interfejsu, który jest niezależny od tego, jak system jest implementowany wewnętrznie. Na przykład enkapsulacja może służyć do ukrywania wartości lub stanu obiektu danych strukturalnych wewnątrz klasy , uniemożliwiając klientom bezpośredni dostęp do nich w sposób, który mógłby ujawnić ukryte szczegóły implementacji lub naruszyć niezmienność stanu utrzymywaną przez metody.
Wszystkie systemy programowania obiektowego (OOP) obsługują enkapsulację, ale enkapsulacja nie jest unikalna dla OOP. Implementacje abstrakcyjnych typów danych , modułów i bibliotek , między innymi, oferują enkapsulację. Podobieństwo zostało wyjaśnione przez teoretyków języka programowania w kategoriach typów egzystencjalnych .
Oznaczający
W zorientowanych obiektowo językach programowania i innych pokrewnych dziedzinach enkapsulacja odnosi się do jednego z dwóch powiązanych, ale odrębnych pojęć, a czasem do ich kombinacji:
- Mechanizm językowy do ograniczania bezpośredniego dostępu do niektórych komponentów obiektu .
- Konstrukcja języka, która ułatwia łączenie danych z metodami (lub innymi funkcjami) operującymi na tych danych.
Niektórzy badacze języków programowania i naukowcy używają pierwszego znaczenia samodzielnie lub w połączeniu z drugim jako wyróżniającej cechy programowania obiektowego , podczas gdy niektóre języki programowania, które zapewniają domknięcia leksykalne, postrzegają enkapsulację jako cechę języka ortogonalną względem orientacji obiektowej.
Druga definicja jest motywowana faktem, że w wielu językach zorientowanych obiektowo i innych pokrewnych dziedzinach komponenty nie są automatycznie ukrywane i można to zmienić; dlatego ukrywanie informacji jest definiowane jako osobne pojęcie przez tych, którzy preferują drugą definicję.
Funkcje enkapsulacji są obsługiwane przy użyciu klas w większości języków zorientowanych obiektowo, chociaż istnieją również inne alternatywy.
Hermetyzacja i dziedziczenie
Autorzy Design Patterns szczegółowo omawiają napięcie między dziedziczeniem a enkapsulacją i stwierdzają, że z ich doświadczenia wynika, że projektanci nadużywają dziedziczenia. Twierdzą, że dziedziczenie często łamie enkapsulację, biorąc pod uwagę, że dziedziczenie naraża podklasę na szczegóły implementacji jej rodzica. Jak opisano w problemie jo-jo , nadużywanie dziedziczenia, a tym samym enkapsulacja, może stać się zbyt skomplikowane i trudne do debugowania.
Ukrywanie informacji
Zgodnie z definicją, że enkapsulacja „może służyć do ukrywania elementów danych i funkcji składowych”, wewnętrzna reprezentacja obiektu jest generalnie ukryta przed widokiem poza definicją obiektu. Zazwyczaj tylko własne metody obiektu mogą bezpośrednio sprawdzać jego pola lub nimi manipulować. Ukrywanie wewnętrznych elementów obiektu chroni jego integralność, uniemożliwiając użytkownikom ustawienie wewnętrznych danych komponentu w nieprawidłowy lub niespójny stan. Przypuszczalną zaletą enkapsulacji jest to, że może zmniejszyć złożoność systemu, a tym samym zwiększyć niezawodność , umożliwiając programistom ograniczenie współzależności między składnikami oprogramowania. [ potrzebne źródło ]
Niektóre języki, takie jak Smalltalk i Ruby , umożliwiają dostęp tylko za pomocą metod obiektowych, ale większość innych (np. C++ , C# , Delphi lub Java ) oferuje programiście pewien stopień kontroli nad tym, co jest ukryte, zazwyczaj za pomocą słów kluczowych, takich jak public
i private
. Norma ISO C++ odnosi się do protected
, private
i public
jako „ specyfikatorów dostępu ” i że nie „ukrywają żadnych informacji”. Ukrywanie informacji odbywa się poprzez dostarczenie skompilowanej wersji kodu źródłowego, który jest połączony za pośrednictwem pliku nagłówkowego.
Niemal zawsze istnieje sposób na obejście takiej ochrony – zwykle za pomocą refleksyjnego API (Ruby, Java, C# itp.), czasem przez mechanizm, taki jak zniekształcanie nazw ( Python ), lub użycie specjalnych słów kluczowych, takich jak friend
w C++. Wyjątkiem są systemy, które zapewniają bezpieczeństwo oparte na możliwościach na poziomie obiektów (zgodnie z modelem możliwości obiektów ) i gwarantują silną enkapsulację.
Przykłady
Ograniczanie pól danych
Języki takie jak C++ , C# , Java , PHP , Swift i Delphi oferują sposoby ograniczania dostępu do pól danych.
Poniżej znajduje się przykład w języku C# , który pokazuje, jak można ograniczyć dostęp do pola danych za pomocą słowa kluczowego private
:
klasa Program { public class Konto { prywatne konto dziesiętne Saldo = 500,00 m ; publiczny dziesiętny CheckBalance () { zwróć to . saldo konta ; } } static void Main () { Konto moje konto = nowe konto (); dziesiętne moje saldo = moje konto . Sprawdź saldo (); /* Ta metoda Main może sprawdzić saldo za pomocą publicznej * metody „CheckBalance” udostępnianej przez klasę „Account” *, ale nie może manipulować wartością „accountBalance” */ } }
Poniżej znajduje się przykład w Javie :
klasa publiczna Pracownik { prywatna BigDecimal pensja = nowa BigDecimal ( 50000,00 ); public BigDecimal getSalary () { zwróć to . wynagrodzenie ; } public static void main () { Pracownik e = nowy Pracownik (); Duża wartość dziesiętna = e . pobierz wynagrodzenie (); } }
Hermetyzacja jest również możliwa w językach nieobiektywnych. Na przykład w C można zadeklarować strukturę w publicznym interfejsie API za pomocą pliku nagłówkowego dla zestawu funkcji, które działają na elemencie danych zawierającym składowe danych, które nie są dostępne dla klientów interfejsu API za pomocą słowa kluczowego extern
.
// plik nagłówkowy "api.h" struct Entity ; // Nieprzezroczysta struktura z ukrytymi elementami // Funkcje API działające na obiektach „Entity” extern struct Entity * open_entity ( int id ); extern int jednostka_procesu ( struktura jednostki * informacje ); extern void close_entity ( struct Entity * info ); // słowa kluczowe extern tutaj są zbędne, ale nie szkodzą. // extern definiuje funkcje, które mogą być wywoływane poza bieżącym plikiem, domyślne zachowanie nawet bez słowa kluczowego
Klienci wywołują funkcje API w celu przydzielania, operowania i zwalniania obiektów o nieprzezroczystym typie danych . Treści tego typu są znane i dostępne jedynie dla realizacji funkcji API; klienci nie mają bezpośredniego dostępu do jego zawartości. Kod źródłowy tych funkcji określa rzeczywistą zawartość struktury:
// Plik implementacji "api.c" #include "api.h" struct Entity { int ent_id ; // numer identyfikacyjny char ent_name [ 20 ]; // Nazwa ... i inni członkowie ... }; // Implementacje funkcji API struct Entity * open_entity ( int id ) { ... } int process_entity ( struct Entity * info ) { ... } void close_entity ( struct Entity * info ) { ... }
Zniekształcenie nazwy
Poniżej znajduje się przykład Pythona , który nie obsługuje ograniczeń dostępu do zmiennych. Jednak konwencja jest taka, że zmienna, której nazwa jest poprzedzona znakiem podkreślenia, powinna być uważana za prywatną.
class Car : def __init__ ( self ) -> Brak : self . _maxspeed = 200 def drive ( self ) -> Brak : print ( f "Maksymalna prędkość to { self . _maxspeed } ." ) redcar = Samochód () redcar . drive () # Spowoduje to wydrukowanie komunikatu „Maksymalna prędkość to 200”. czerwony samochód _maxspeed = 10 czerwony samochód . drive () # Spowoduje to wydrukowanie komunikatu „Maksymalna prędkość to 10”.
Zobacz też
- Dziedziczenie (programowanie obiektowe)
- Programowanie obiektowe
- Wzorzec projektowy oprogramowania
- Wzór elewacji