Enkapsulacja (programowanie komputerowe) - Encapsulation (computer programming)

W programowaniu obiektowym (OOP) enkapsulacja odnosi się do łączenia danych za pomocą metod operujących na tych danych lub ograniczenia bezpośredniego dostępu do niektórych składników obiektu. Enkapsulacja 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.

Publicznie dostępne metody są zazwyczaj udostępniane w klasie w celu bardziej abstrakcyjnego dostępu do stanu lub modyfikowania go. W praktyce czasami stosowane są metody (tzw. „gettery” i „setters” ) w celu uzyskania pośredniego dostępu do wartości, ale chociaż niekoniecznie stanowi to naruszenie abstrakcyjnej enkapsulacji, często są one uważane za drogowskaz potencjalnie słabego programowania obiektowego (OOP) praktyka projektowa ( Antywzór ).

Ten mechanizm nie jest unikalny dla OOP. Podobną formę enkapsulacji oferują implementacje abstrakcyjnych typów danych , np. modułów . Podobieństwo zostało wyjaśnione przez teoretyków języka programowania w kategoriach typów egzystencjalnych .

Oznaczający

W obiektowych językach programowania i innych powiązanych 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 składnikó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 cechą wyróżniającą programowanie obiektowe , podczas gdy niektóre języki programowania, które zapewniają domknięcia leksykalne, postrzegają enkapsulację jako cechę języka ortogonalną do orientacji obiektowej.

Druga definicja wynika z faktu, że w wielu językach obiektowych i innych powiązanych dziedzinach komponenty nie są automatycznie ukrywane i można to przesłonić; dlatego ukrywanie informacji jest definiowane jako odrębne 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 obiektowych, chociaż istnieją również inne alternatywy.

Hermetyzacja i dziedziczenie

Autorzy Wzorców Projektowych szczegółowo omawiają napięcie między dziedziczeniem a enkapsulacją i stwierdzają, że w swoim doświadczeniu projektanci nadużywają dziedziczenia. Twierdzą, że dziedziczenie często łamie enkapsulację, biorąc pod uwagę, że dziedziczenie udostępnia podklasę szczegółom implementacji jej rodzica. Jak opisuje problem jo-jo , nadużywanie dziedziczenia, a tym samym enkapsulacji, może stać się zbyt skomplikowane i trudne do debugowania.

Ukrywanie informacji

Zgodnie z definicją, że hermetyzacja „może być używana do ukrywania elementów danych i funkcji elementów członkowskich”, wewnętrzna reprezentacja obiektu jest zwykle ukryta przed widokiem poza definicją obiektu. Zazwyczaj tylko własne metody obiektu mogą bezpośrednio sprawdzać lub manipulować jego polami. Ukrycie elementów wewnętrznych obiektu chroni jego integralność, uniemożliwiając użytkownikom ustawienie wewnętrznych danych komponentu w nieprawidłowy lub niespójny stan. Domniemaną zaletą enkapsulacji jest to, że może ona zmniejszyć złożoność systemu, a tym samym zwiększyć niezawodność , umożliwiając programiście ograniczenie współzależności między komponentami oprogramowania.

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 publici private. Norma ISO C ++ oznacza protected, privatei publicjako „ specyfikatorów dostępu ” i że nie są „ukryć wszelkie informacje”. Ukrywanie informacji odbywa się poprzez dostarczenie skompilowanej wersji kodu źródłowego, który jest połączony poprzez plik nagłówkowy.

Prawie zawsze istnieje sposób na obejście takiej ochrony – zazwyczaj przez odbicie API (Ruby, Java, C#, itp.), czasem przez mechanizm, taki jak przerabianie nazw ( Python ), lub użycie specjalnych słów kluczowych, jak friendw C++. Wyjątkiem są systemy, które zapewniają zabezpieczenia oparte na możliwościach na poziomie obiektu (zgodne z modelem możliwości obiektowych ) i gwarantują silną hermetyzację.

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ą privatesłowa kluczowego:

class Program
{
    public class Account
    {
        private decimal accountBalance = 500.00m;

        public decimal CheckBalance()
        {
            return this.accountBalance;
        }
    }

    static void Main()
    {
        Account myAccount = new Account();
        decimal myBalance = myAccount.CheckBalance();

        /* This Main method can check the balance via the public
         * "CheckBalance" method provided by the "Account" class 
         * but it cannot manipulate the value of "accountBalance" */
    }
}

Poniżej znajduje się przykład w Javie :

public class Employee {
    private BigDecimal salary = new BigDecimal(50000.00);
    
    public BigDecimal getSalary() {
        return this.salary;
    }

    public static void main() {
        Employee e = new Employee();
        BigDecimal sal = e.getSalary();
    }
}

Enkapsulacja jest również możliwa w językach niezorientowanych obiektowo. W C , na przykład, strukturę można zadeklarować w publicznym API za pomocą pliku nagłówkowego dla zestawu funkcji, które operują na elemencie danych zawierającym elementy danych, które nie są dostępne dla klientów API za pomocą externsłowa kluczowego.

// Header file "api.h"

struct Entity;          // Opaque structure with hidden members

// API functions that operate on 'Entity' objects
extern struct Entity *  open_entity(int id);
extern int              process_entity(struct Entity *info);
extern void             close_entity(struct Entity *info);
// extern keywords here are redundant, but don't hurt.
// extern defines functions that can be called outside the current file, the default behavior even without the keyword

Klienci wywołują funkcje API, aby alokować, operować i cofać alokację obiektów o nieprzezroczystym typie danych . Treści tego typu są znane i dostępne tylko dla implementacji funkcji API; klienci nie mają bezpośredniego dostępu do jego zawartości. Kod źródłowy tych funkcji definiuje rzeczywistą zawartość struktury:

// Implementation file "api.c"

#include "api.h"

struct Entity {
    int     ent_id;         // ID number
    char    ent_name[20];   // Name
    ... and other members ...
};

// API function implementations
struct Entity * open_entity(int id)
{ ... }

int process_entity(struct Entity *info)
{ ... }

void close_entity(struct Entity *info)
{ ... }

Manglowanie nazw

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 podkreśleniem, powinna być uważana za prywatną.

class Car: 
    def __init__(self) -> None:
        self._maxspeed = 200
 
    def drive(self) -> None:
        print(f"Maximum speed is {self._maxspeed}.")
 
redcar = Car()
redcar.drive()  # This will print 'Maximum speed is 200.'

redcar._maxspeed = 10
redcar.drive()  # This will print 'Maximum speed is 10.'

Zobacz też

Bibliografia