language:

Articles on optimization by Adrian Kalbarczyk

„Half of a success is the skill to ask right questions”

, by unknown

Paradygmat obiektowy oparty na prototypach

Wstęp

Najbardziej rozpowszechnioną metodą projektowania programów obiektowych jest podział zbioru wszystkich obiektów występujących w systemie na pewne klasy abstrakcji. Zachowanie oraz właściwości (będące osobnymi częściami definicji) obiektów zapisywane są za pomocą tak zwanych klas. W wielu przypadkach okazuje się, że klasy abstrakcji są do siebie podobne. Aby uzyskać kompaktowość deklaracji pewne klasy są łączone w zbiory pod względem podobieństwa, a następnie poddawane hierarchizacji. Wybiera się zachowanie (dalej metody) i właściwości (dalej atrybuty), które się powtarzają w obiektach całego podzbioru i tworzy się klasę je zawierającą. Jeśli nie istnieją obiekty, które da się zapisać przy pomocy wyłącznie tych pól, klasę nazywamy abstrakcyjną. Podzbiór złożony z pozostałych obiektów zostaje podzielony na kolejne podzbiory, w których wybiera się metody oraz atrybuty cechujące dany podzbiór. Każdy z nich zostaje opisany jako klasa dziedzicząca po klasie nadzbioru. Czynność tą powtarzamy rekurencyjnie dopóki wszystkie podzbiory nie są puste.

W złożonych projektach programistycznych jest bardzo prawdopodobne, że liczba klas (będących przecież w relacjach z innymi klasami - por. diagramy UML), będzie bardzo duża, a wręcz może się okazać, że dla każdego obiektu zaistnieje potrzeba opisania go przez inną klasę. W wielu przypadkach okazuje się, że czas poświęcony na projektowanie klas i ich hierarchii znacznie przewyższa czas kodowania programów. Jeśli weźmiemy pod uwagę fakt, że raz napisana hierarchia jest bardzo nieelastyczna (występują duże problemy przy nanoszeniu jakichkolwiek zmian), dojdziemy do wniosku, że warto poznać rozwiązania alternatywne.

Obiektowość "czysta"

Obiektowość sama w sobie jest bardzo naturalna. Już starożytni zastanawiali się nad jej istotą i jest wiele szkół na temat tego czym one są. W informatyce możemy przyjąć uproszczoną i nieco zmienioną wersję jednej z nich mówiącej o tym, że obiekt to pewien byt posiadający swoje atrybuty. W definicji jest wyraźnie powiedziane, że wartością atrybutu nie może być inny obiekt. Zmiana polega właśnie na tym, że pomijamy ten warunek. Obiekty zawierają jedynie atrybuty widoczne na zewnątrz obiektu (w nomenklaturze obiektowej: mają zasięg publiczny). Zakładamy, że model posiada tylko jeden typ wartości atrybutu i jest nim obiekt, natomiast rozróżnienie istnieje na poziomie typów obiektów. Ten typ obiektu będziemy nazywali dalej złożonym.

Obiekt w najczystszej postaci jest często implementowany jako słownik posiadający unikatowe nazwy atrybutów. Jak już wspomnieliśmy, wartościami atrybutów może być dowolny inny obiekt, w tym także obiekt złożony (a także on sam). Wprowadźmy tu pojęcie obiektu prostego.

Obiekt prosty - obiekt przygotowany przez twórców języka programowania lub jego implementacji; obiekt nie dziedziczący po obiekcie głównym; obiekt przekazywany przez wartość (nie przez referencję); będący wartością atrybutu z definicji filozoficznej; podobny w swej istocie do aksjomatu matematycznego.

Definicja ta zależy od języka programowania (i implementacji). Intuicyjnie, jest to najprostszy obiekt taki jak liczba czy napis lub obiekt główny (o którym poniżej). Obiekty można zilustrować za pomocą drzewa, którego liśćmi są obiekty proste, a pozostałymi wierzchołkami - obiekty złożone. Obiekty proste powinny posiadać swój typ tak aby mogły przechowywać dane natomiast atrybuty obiektów złożonych muszą wskazywać na "obiekt" a nie na "obiekt typu..." (innymi słowy: typizowane dynamicznie).

Metody są realizowane przez funkcje. W wielu językach programowania są one obywatelami pierwszej klasy, czyli wskaźnik do nich jest przechowywany w zmiennej tak jak do obiektów. Ponieważ w modelu nie ma typów innych niż obiekt, funkcja także jest obiektem. Jej zmienne mogą być widoczne na zewnątrz funkcji (stając się atrybutami).

Cechą obiektów jest możliwość dodawania, usuwania i zmiany ich atrybutów i wartości w czasie wykonania programu. Dzięki temu w modelu tym, obiekty służą jako kontenery danych, którymi można dowolnie manipulować. Ponadto każdy obiekt traktowany jest indywidualnie (nie podlega klasyfikacji), co sprawia, że łatwiej o zmianę struktury obiektów w przyszłości.

Łatwo zauważyć, że w tym modelu dziedziczenie łatwo zrealizować poprzez kopiowanie atrybutów z jednego obiektu do innego, wielodziedziczenie przez kopiowanie atrybutów wielu obiektów do innego i replikację obiektu przez kopiowanie atrybutów wraz z jego wartościami (rekurencyjnie lub nie, w zależności od potrzeb). Wskaźniki do superobiektów (czyli takich, z których obiekt powstał) można zapisać jako wartość jednego z atrybutów obiektu.

W ogólności, powyższe fakty oznaczają bardzo dużą elastyczność modelu. Dla małych projektów można przyjąć proste zasady wytwarzania oprogramowania, które pozwolą na poświęcenie niemal całego czasu projektu na programowanie. Dla dużych projektów istnieją metody symulacji klasowości, a także inne równie sformalizowane metody zarządzania obiektami. Najważniejsze jest to, że nie ma postawionych odgórnych zasad co do projektowania i kodowania programów.

Prototypowość

W poprzednim rozdziale przedstawiliśmy pewien model, prototypowość jest jego implementacją.

Prototypowość - mechanizm, implementowany wewnątrz środowiska wykonania, pozwalający na uporządkowanie zasad i przyspieszenie wykonania dziedziczenia obiektów.

Wyróżniony jest pusty obiekt główny (podstawowy). Służy on jako prototyp dla obiektów użytkownika.

Prototyp - obiekt służący jako szablon przy tworzeniu nowego obiektu.

Prototypem może być dowolny obiekt dziedziczący po obiekcie głównym. Jak widać, głównym zadaniem prototypowości jest umożliwienie szybkiego kopiowania atrybutów obiektu w tle. Mechanizm dziedziczenia z poprzedniego rozdziału jest o wiele silniejszy, jednak w wielu miejscach warto użyć prototypów także dla standaryzacji wyglądu kodu. Inną zaletą jest to, że nie musimy wymyślać własnych metod dziedziczenia, ręcznie łączyć go z superobiektem itp.

Prototypowość a inne paradygmaty programowania

W dużej mierze prototypowość jest kompatybilna z programowaniem funkcyjnym. Dla przykładu w OCamlu istnieją bardzo podobne metodologie manipulacji i przechowywania danych. Wiele z cech języków funkcyjnych jest niezbędna do poprawnego działania obiektowości prostej (szczególnie pierwszoklasowość funkcji).

Jeśli chodzi o klasowe podejście do obiektowości, można powiedzieć, że jest to inny biegun. Nie ma praktycznie żadnych cech wspólnych. Same obiekty występują w takiej czy innej formie w każdym paradygmacie (nawet strukturalnym), więc nie należy ich brać jako wyznacznik podobieństwa. W SmallTalku klasy są też obiektami co sprawia, że podobieństw jest więcej, ale zasady programowania są diametralnie inne.

Podsumowanie

Przedstawienie wszystkich zalet i metod użycia prototypów (lub obiektowości prostej) zajęłoby wiele kilobajtów tekstu. W ramach tego artykułu chciałem zwrócić uwagę czytelnika na alternatywę dla programowania klasowego, tak aby zastanowił się czy przegadany paradygmat jest rzeczywiście dla niego i czy nie warto sięgnąć po coś lżejszego. Jeśli chodzi o implementację, najważniejszą jest JavaScript. Jest on dostępny w różnych formach od przeglądarek internetowych przez skrypty pisane dla Androida, Plasmy, aż do interpretera/kompilatora napisanego w Javie o nazwie Rhino. Pozostałe języki prototypowe mają marginalne znaczenie i ich składnia jest lekko mówiąc ekscentryczna, ale wyróżniają się tu Self oraz Lisaac.

Część zasad programowania czysto-obiektowego można zastosować w programowaniu w Pythonie oraz OCamlu jeśli zamiast słowa obiekt użyjemy odpowiednio: słownik i zestaw typów.

report mistake on this page
Report mistake on this page
Here you can let me know about errors on page. Reporting misspelings, grammar errors are very welcomed. If you are experiencing serious site bug please write how it is reproducable. You don't need to fill any of these fields!