Gdy struktura danych (na przykład kolejka) jest implementowana przy użyciu języka OOP, niektórzy członkowie struktury danych muszą być prywatni (na przykład liczba elementów w kolejce).
Kolejka może być również zaimplementowana w języku proceduralnym przy użyciu struct
zestawu funkcji działających w systemie struct
. Jednak w języku proceduralnym nie można uczynić członków struct
prywatnymi. Czy członkowie struktury danych zaimplementowani w języku proceduralnym pozostali publiczni, czy też istnieje jakiś sposób na uczynienie ich prywatnymi?
object-oriented
data-structures
history
Krzysztof
źródło
źródło
Odpowiedzi:
OOP nie wynalazł enkapsulacji i nie jest synonimem enkapsulacji. Wiele języków OOP nie ma modyfikatorów dostępu w stylu C ++ / Java. Wiele języków innych niż OOP oferuje różne techniki oferujące enkapsulację.
Klasycznym podejściem do enkapsulacji są zamknięcia , stosowane w programowaniu funkcjonalnym . Jest to znacznie starsze niż OOP, ale jest w pewnym sensie równoważne. Np. W JavaScript możemy stworzyć taki obiekt:
Powyższy
plus2
obiekt nie ma elementu, który umożliwiałby bezpośredni dostęp dox
- jest całkowicie zamknięty.add()
Metoda jest zamknięcie nax
zmiennej.Język C obsługuje pewne rodzaje enkapsulacji za pomocą mechanizmu pliku nagłówkowego , w szczególności technikę nieprzezroczystego wskaźnika . W C można zadeklarować nazwę struktury bez definiowania jej członków. W tym momencie nie można użyć żadnej zmiennej typu tej struktury, ale możemy swobodnie używać wskaźników do tej struktury (ponieważ rozmiar wskaźnika struktury jest znany w czasie kompilacji). Weźmy na przykład ten plik nagłówkowy:
Możemy teraz napisać kod korzystający z tego interfejsu Addera, bez dostępu do jego pól, np .:
I oto byłyby całkowicie zamknięte szczegóły implementacji:
Istnieje również klasa modułowych języków programowania , która koncentruje się na interfejsach na poziomie modułu. Rodzina języków ML włącznie OCaml zawiera ciekawe podejście do modułów zwanych funktorami . OOP przyćmiło i w dużej mierze przysłoniło programowanie modułowe, ale wiele rzekomych zalet OOP dotyczy bardziej modułowości niż orientacji obiektowej.
Istnieje również spostrzeżenie, że klasy w językach OOP, takich jak C ++ lub Java, często nie są używane dla obiektów (w sensie podmiotów, które rozwiązują operacje poprzez późne wiązanie / dynamiczne wysyłanie), a jedynie do abstrakcyjnych typów danych (w których definiujemy publiczny interfejs, który ukrywa wewnętrzne szczegóły wdrożenia). Artykuł „ Understanding Data Abstraction, Revisited” (Cook, 2009) omawia tę różnicę bardziej szczegółowo.
Ale tak, wiele języków nie ma żadnego mechanizmu enkapsulacji. W tych językach członkowie struktury są publicznie dostępni. Co najwyżej istniałaby konwencja nazewnictwa zniechęcająca do użycia. Np. Myślę, że Pascal nie miał użytecznego mechanizmu enkapsulacji.
źródło
Adder self = malloc(sizeof(Adder));
? Jest powód, dla którego typowanie wskaźnikówsizeof(TYPE)
jest na ogół niezadowolone.sizeof(*Adder)
, ponieważ*Adder
nie jest typem, tak jak*int *
nie jest typem. WyrażenieT t = malloc(sizeof *t)
jest zarówno idiomatyczne, jak i poprawne. Zobacz moją edycję.private static
zmiennym w Javie. Podobnie do C można użyć nieprzejrzystych wskaźników do przekazywania danych w Pascalu bez deklarowania, co to było. Klasyczny MacOS używał wielu nieprzejrzystych wskaźników, ponieważ publiczne i prywatne części rekordu (struktura danych) mogą być przekazywane razem. Pamiętam, że Menedżer okien robił wiele z tego, ponieważ niektóre części rekordu okna były publiczne, ale niektóre informacje wewnętrzne zostały również uwzględnione._private_member
ioutput_property_
, lub bardziej zaawansowanych technik tworzenia obiektów możliwych do przypisania.Po pierwsze, podejście proceduralne kontra obiektowe nie ma nic wspólnego z publicznym czy prywatnym. Wiele języków obiektowych nie ma pojęcia o kontroli dostępu.
Po drugie, w „C” - które większość ludzi nazwałaby proceduralnymi, a nie obiektowymi, istnieje wiele sztuczek, których można użyć, aby skutecznie uczynić rzeczy prywatnymi. Bardzo często stosuje się nieprzezroczyste wskaźniki (np. Void *). Lub - możesz przekazać deklarację obiektu i po prostu nie definiować go w pliku nagłówkowym.
foo.h:
foo.c:
Spójrz na Windows SDK! Wykorzystuje HANDLE i UINT_PTR, a takie rzeczy są ogólnymi uchwytami pamięci używanej w interfejsach API - skutecznie czyniąc implementacje prywatnymi.
źródło
„Nieprzezroczyste typy danych” były dobrze znaną koncepcją, gdy 30 lat temu uzyskałem stopień informatyki. Nie obejmowaliśmy OOP, ponieważ nie było to wtedy w powszechnym użyciu, a „programowanie funkcjonalne” uznano za bardziej poprawne.
Modula-2 miała dla nich bezpośrednie wsparcie, patrz https://www.modula2.org/reference/modules.php .
Lewis Pringle już wyjaśnił, w jaki sposób można deklarować strukturę w C w przód. W przeciwieństwie do Module-2, do utworzenia obiektu trzeba było zapewnić funkcję fabryki. ( Metody wirtualne były również łatwe do wdrożenia w C , ponieważ pierwszy element struktury był wskaźnikiem innej struktury zawierającej wskaźniki funkcji do metod).
Często stosowano także konwencję. Na przykład żadne pole zaczynające się od „_” nie powinno być dostępne poza plikiem, który był właścicielem danych. Łatwo to wymusić dzięki stworzeniu niestandardowych narzędzi sprawdzających.
Każdy projekt na dużą skalę, nad którym pracowałem (zanim przeszedłem do C ++, a następnie C #) posiadał system zapobiegający dostępowi do „prywatnych” danych przez niewłaściwy kod. To było tylko trochę mniej znormalizowane niż obecnie.
źródło
Uwaga: istnieje wiele języków OO bez wbudowanej możliwości oznaczania członków jako prywatnych. Można to zrobić umownie, bez potrzeby kompilatora do egzekwowania prywatności. Na przykład ludzie często poprzedzają prywatne zmienne znakiem podkreślenia.
Istnieją techniki utrudniające dostęp do zmiennych „prywatnych”, z których najczęstszym jest idiom PIMPL . To umieszcza twoje prywatne zmienne w osobnej strukturze, z tylko wskaźnikiem przydzielonym w twoich publicznych plikach nagłówkowych. Oznacza to dodatkowe dereferencje i obsadę, aby uzyskać dowolne zmienne prywatne, coś takiego
((private_impl)(obj->private))->actual_value
, co staje się denerwujące, więc w praktyce jest rzadko używane.źródło
Struktury danych nie miały „elementów”, tylko pola danych (zakładając, że był to typ rekordu). Widoczność była zwykle ustawiana dla całego typu. Jednak może to nie być tak ograniczające, jak myślisz, ponieważ funkcje nie były częścią rekordu.
Cofnijmy się i zdobądźmy trochę historii tutaj ...
Dominujący paradygmat programowania przed OOP nazwano programowaniem strukturalnym . Początkowym głównym celem tego było uniknięcie stosowania nieustrukturyzowanych instrukcji skoku („goto”). Jest to paradygmat zorientowany na przepływ sterowania (podczas gdy OOP jest bardziej zorientowany na dane), ale nadal było naturalnym rozszerzeniem jego próby logicznej struktury danych, tak jak kod.
Kolejnym odgałęzieniem programowania strukturalnego było ukrywanie informacji , pomysł, że implementacje struktury kodu (które prawdopodobnie będą się zmieniać dość często) powinny być oddzielone od interfejsu (co idealnie nie zmieni się prawie tak bardzo). Teraz jest to dogmat, ale w dawnych czasach wiele osób uważało, że lepiej jest, aby każdy programista poznał szczegóły całego systemu, więc był to kiedyś kontrowersyjny pomysł. Oryginalna edycja Miesiąca Mitycznego Człowieka Brook'a faktycznie sprzeciwiła się ukrywaniu informacji.
Późniejsze języki programowania zaprojektowane wyraźnie jako dobre Języki programowania strukturalnego (na przykład Modula-2 i Ada) generalnie zawierały ukrywanie informacji jako podstawową koncepcję, zbudowaną wokół pewnego rodzaju koncepcji spójnego ułatwienia funkcji (i wszelkich typów, stałych i obiekty, których mogą potrzebować). W Modula-2 były one nazywane „modułami”, w Adzie „pakietami”. Wiele współczesnych języków OOP nazywa tę samą koncepcję „przestrzeniami nazw”. Te przestrzenie nazw były organizacyjną podstawą rozwoju w tych językach i dla większości celów mogą być używane podobnie jak klasy OOP (oczywiście bez rzeczywistego wsparcia dla dziedziczenia).
Tak więc w Modula-2 i Adzie (83) możesz zadeklarować dowolną procedurę, typ, stałą lub obiekt w przestrzeni nazw prywatnej lub publicznej, ale jeśli masz typ rekordu, nie ma (łatwego) sposobu na zadeklarowanie niektórych pól rekordów jako publicznych i inne prywatne. Albo cała twoja płyta jest publiczna, albo nie.
źródło
object.method()
Inwokacja jest po prostu cukier syntaktyczny. Ważne IMHO - patrz zasada jednolitego dostępu / odniesienia Meyera - ale wciąż tylko cukier syntaktyczny.object.method()
jako alternatywną formęmethod(object, ...)
dla ludzi, którzy po prostu nie mogli dokonać skoku koncepcyjnego.W C można już przekazywać wskaźniki do zadeklarowanych, ale niezdefiniowanych typów, jak powiedzieli inni, w efekcie ograniczając dostęp do wszystkich pól.
Możesz także mieć funkcje prywatne i publiczne na zasadzie moduł-moduł. Funkcje zadeklarowane jako statyczne w pliku źródłowym nie są widoczne na zewnątrz, nawet jeśli spróbujesz odgadnąć ich nazwę. Podobnie, możesz mieć statyczne zmienne globalne na poziomie pliku, co jest ogólnie złą praktyką, ale pozwala na izolację na poziomie modułu.
Prawdopodobnie ważne jest podkreślenie, że ograniczenie dostępu jako dobrze znormalizowana konwencja, a nie konstrukcja wymuszona językiem, działa dobrze (patrz Python). Co więcej, ograniczenie dostępu do pól obiektowych zawsze chroni programistę tylko wtedy, gdy istnieje potrzeba zmiany wartości danych wewnątrz obiektu po utworzeniu. Który już jest zapachem kodu. Prawdopodobnie
const
słowo kluczowe C, a w szczególności C ++ dla metod i argumentów funkcyjnych, jest znacznie większą pomocą dla programisty niż raczej słaba Javafinal
.źródło
static
globalne dane i operacje (co oznaczało, że nie zostały przedstawione linkerowi do użycia z innych kompilacji). Można w sposób wiarygodny argumentować, że C wsparło dobre praktyki projektowania oprogramowania, poza tym był to w zasadzie hack i nie był częścią oryginalnego projektu języka z 1972 r.Jeśli twoja definicja Public to możliwość dostępu do implementacji i danych / właściwości za pomocą własnego kodu w dowolnym momencie, odpowiedź brzmi: Tak . Został jednak wyodrębniony na różne sposoby - w zależności od języka.
Mam nadzieję, że zwięźle odpowiedziała na twoje pytanie.
źródło
Oto bardzo prosty kontrprzykład: w Javie
interface
definiują obiekty, aleclass
nie. Aclass
definiuje abstrakcyjny typ danych, a nie obiekt.Ergo, za każdym razem, gdy używasz
private
wclass
Javie, masz przykład struktury danych z elementami prywatnymi, która nie jest zorientowana obiektowo.źródło