Zasada jednolitego dostępu
jednolitego dostępu w programowaniu komputerowym została przedstawiona przez Bertranda Meyera (pierwotnie w Object-Oriented Software Construction ). Stwierdza, że „Wszystkie usługi oferowane przez moduł powinny być dostępne za pośrednictwem jednolitej notacji, która nie zdradza, czy są one realizowane poprzez przechowywanie, czy obliczenia”. Zasada ta dotyczy ogólnie składni obiektowych języków programowania . W prostszej formie stwierdza, że nie powinno być różnic składniowych między pracą z atrybutem , wstępnie obliczoną właściwością lub metodą / zapytaniem obiektu.
Podczas gdy większość przykładów skupia się na „odczytywanym” aspekcie zasady (tj. pobieraniu wartości), Meyer pokazuje, że implikacje „zapisu” (tj. modyfikacja wartości) zasady są trudniejsze do omówienia w swoim comiesięcznym felietonie na Oficjalna strona języka programowania Eiffla .
Wyjaśnienie
Problem, którym zajmuje się Meyer, dotyczy utrzymania dużych projektów oprogramowania lub bibliotek oprogramowania. Czasami podczas opracowywania lub utrzymywania oprogramowania konieczna jest, po wprowadzeniu dużej ilości kodu, zmiana klasy lub obiektu w sposób, który przekształca to, co było po prostu dostępem do atrybutu, w wywołanie metody. Języki programowania często używają innej składni dostępu do atrybutów i wywoływania metody (np. obiekt.coś
kontra obiekt.coś()
). Zmiana składni wymagałaby, w popularnych wówczas językach programowania, zmiany kodu źródłowego we wszystkich miejscach, w których zastosowano atrybut. Może to wymagać zmiany kodu źródłowego w wielu różnych lokalizacjach w bardzo dużej ilości kodu źródłowego. Lub, co gorsza, jeśli zmiana dotyczy biblioteki obiektów używanej przez setki klientów, każdy z tych klientów musiałby znaleźć i zmienić wszystkie miejsca, w których atrybut był używany we własnym kodzie, i ponownie skompilować swoje programy.
Przejście w odwrotną stronę (od metody do prostego atrybutu) naprawdę nie stanowiło problemu, ponieważ zawsze można po prostu zachować funkcję i sprawić, by po prostu zwracała wartość atrybutu.
Meyer dostrzegł potrzebę pisania kodu przez programistów w taki sposób, aby zminimalizować lub wyeliminować kaskadowe zmiany w kodzie, które wynikają ze zmian, które przekształcają atrybut obiektu w wywołanie metody lub odwrotnie. W tym celu opracował zasadę jednolitego dostępu.
Wiele języków programowania nie obsługuje ściśle UAP, ale obsługuje jego formy. Właściwości, które są dostępne w wielu językach programowania, rozwiązują problem, który Meyer rozwiązywał w swoim UAP w inny sposób. Zamiast zapewniać pojedynczą, jednolitą notację, właściwości umożliwiają wywołanie metody obiektu przy użyciu tej samej notacji, która jest używana do uzyskiwania dostępu do atrybutów. Oddzielna składnia wywołania metody jest nadal dostępna.
Przykład UAP
Jeśli język używa składni wywołania metody, może wyglądać mniej więcej tak.
// Załóżmy, że print wyświetla zmienną przekazaną do niej, z nawiasami lub bez // Ustaw atrybut Foo 'bar' na wartość 5. Foo.bar(5) print Foo.bar()
Po wykonaniu powinno wyświetlić się:
5
To, czy Foo.bar(5)
wywołuje funkcję, czy po prostu ustawia atrybut, jest ukryte przed wywołującym. Podobnie to, czy funkcja Foo.bar()
po prostu pobiera wartość atrybutu, czy też wywołuje funkcję w celu obliczenia zwróconej wartości, jest szczegółem implementacji ukrytym przed wywołującym.
Jeśli język używa składni atrybutów, składnia może wyglądać tak.
Foo.bar = 5 drukuj Foo.bar
Ponownie, niezależnie od tego, czy metoda jest wywoływana, czy też wartość jest po prostu przypisana do atrybutu, jest ukrywana przed wywołującą metodą.
Problemy
Jednak sam UAP może powodować problemy, jeśli jest używany w miejscach, w których różnice między metodami dostępu nie są pomijalne, na przykład gdy zwracana wartość jest kosztowna do obliczenia lub wyzwala operacje pamięci podręcznej.
Przykłady językowe
Pyton
Właściwości Pythona mogą być używane, aby umożliwić wywołanie metody z taką samą składnią jak dostęp do atrybutu. Podczas gdy UAP Meyera miałby jedną notację zarówno dla dostępu do atrybutów, jak i wywołania metody (składnia wywołania metody), język obsługujący właściwości nadal obsługuje oddzielne notacje dla dostępu do atrybutów i metod. Właściwości pozwalają na użycie notacji atrybutu, ale ukrywają fakt, że metoda jest wywoływana zamiast zwykłego pobierania lub ustawiania wartości.
W związku z tym Python pozostawia opcję przestrzegania UAP indywidualnemu programiście. Wbudowana @property
zapewnia prosty sposób dekorowania dowolnej metody w składni dostępu do atrybutów, eliminując w ten sposób różnice składniowe między wywołaniami metod a dostępami do atrybutów.
W Pythonie możemy mieć kod uzyskujący dostęp do obiektu Egg
, który można zdefiniować w taki sposób, że waga i kolor są prostymi atrybutami, jak w poniższym
""" >>> jajko = Jajko(4.0, "biały") >>> jajko.kolor = "zielony" >>> print(jajko) Jajo(4.0, zielony) """ klasa Jajo : def __init__ ( self , waga , kolor ) -> Brak : self . waga = waga siebie . color = color def __str__ ( self )
-> str : return f " { __klasa__ . __nazwa__ } ( { własny . waga } , { własny . kolor } )"
Lub obiekt Egg mógłby używać właściwości i zamiast tego wywoływać metody pobierające i ustawiające
# ...(snip)... class Egg : def __init__ ( self , weight_oz : float , color_name : float ) -> Brak : self . waga = waga_oz siebie . color = color_name @property def color ( self ) -> str : '''Kolor jajka'''
powrót do_color_str ( self . _color_rgb ) @kolor . setter def color ( self , color_name : str ) -> Brak : self . _color_rgb = to_rgb ( nazwa_koloru ) @property def weight ( self ) -> float : '''Waga w uncjach'''
zwrócić siebie . _waga_gram / 29,3 @waga . setter def weight ( self , weight_oz : float ) -> Brak : self . _waga_gram = 29,3 * waga_oz # ...(odcinek)...
import webcolors # class Egg: def to_color_str ( rgb : webcolors . IntegerRGB ) -> str : try : return webcolors . rgb_to_name ( rgb ) z wyjątkiem ValueError : zwraca kolory internetowe . rgb_to_hex ( rgb ) def to_rgb ( nazwa_koloru :
str ) -> kolory internetowe . IntegerRGB : try : zwraca kolory internetowe . name_to_rgb ( nazwa_koloru ) z wyjątkiem ValueError : zwraca kolory internetowe . hex_to_rgb ( nazwa_koloru ) if __name__ == "__main__" : import doctest doctest . mod testowy ()
|
Niezależnie od tego, w jaki sposób zdefiniowano Egg , kod wywołujący może pozostać taki sam.
Implementacja Egg
może przełączać się z jednej formy do drugiej bez wpływu na kod korzystający z klasy Egg. Języki, które implementują UAP, również mają tę właściwość.
Rubin
Rozważ następujące
y = Jajko . nowy ( „zielony” ) y . color = "Biały" stawia y . kolor
Teraz klasę Egg można zdefiniować w następujący sposób
class Egg attr_accessor :color def initialize ( color ) @color = color end end
Powyższy początkowy segment kodu działałby dobrze, gdyby jajko zostało zdefiniowane jako takie. Klasę Egg można również zdefiniować jak poniżej, gdzie kolor jest metodą. Kod wywołujący nadal działałby, niezmieniony, gdyby Egg miał być zdefiniowany w następujący sposób.
class Egg def initialize ( kolor ) @rgb_color = to_rgb ( kolor ) end def color to_color_name ( @rgb_color ) end def color= ( color ) @rgb_color = to_rgb ( color ) end private def to_rgb ( color_name ) ..... end def
to_color_name ( kolor ) .... koniec koniec
Zwróć uwagę, że chociaż kolor
wygląda jak atrybut w jednym przypadku, a para metod w następnym, interfejs do klasy pozostaje taki sam. Osoba utrzymująca klasę Egg może przechodzić z jednej formy do drugiej bez obawy o złamanie kodu dzwoniącego. Ruby podąża za poprawionym UAP, attr_accessor :color
działa tylko jako cukier składniowy do generowania metod akcesora/settera dla color
. W języku Ruby nie ma możliwości pobrania zmiennej instancji z obiektu bez wywołania na niej metody.
Ściśle mówiąc, Ruby nie podąża za oryginalnym UAP Meyera, ponieważ składnia dostępu do atrybutu różni się od składni wywoływania metody. Ale tutaj dostęp do atrybutu zawsze będzie faktycznie odbywał się za pośrednictwem funkcji, która często jest generowana automatycznie. Tak więc w istocie każdy rodzaj dostępu wywołuje funkcję, a język jest zgodny ze zmienioną zasadą jednolitego dostępu Meyera.
C#
Język C# obsługuje właściwości klas , które umożliwiają definiowanie operacji pobierania
i ustawiania (
pobierających i ustawiających ) dla zmiennej składowej. Składnia uzyskiwania dostępu lub modyfikowania właściwości jest taka sama, jak uzyskiwania dostępu do dowolnej innej zmiennej składowej klasy, ale rzeczywista implementacja tego może być zdefiniowana jako prosty dostęp do odczytu/zapisu lub jako kod funkcjonalny.
klasa publiczna Foo { prywatny ciąg _nazwa ; // Właściwość public int Rozmiar { get ; // Getter ustawia ; // Setter } // Właściwość public string Nazwa { get { return _name ; } // Getter set { _name = value ; } // Seter } }
W powyższym przykładzie klasa Foo
zawiera dwie właściwości, Size
i Name
. Właściwość Size jest liczbą całkowitą, którą można odczytać (pobrać) i zapisać (ustawić) .
Podobnie właściwość Name
jest ciągiem znaków, który również można odczytywać i modyfikować, ale jej wartość jest przechowywana w oddzielnej (prywatnej) zmiennej klasy _name
.
Pominięcie operacji set
w definicji właściwości powoduje, że właściwość jest tylko do odczytu, natomiast pominięcie operacji get
powoduje, że jest ona tylko do zapisu.
Użycie właściwości wykorzystuje UAP, jak pokazano w poniższym kodzie.
public Foo CreateFoo ( rozmiar int , nazwa ciągu ) { var foo = new Foo (); fuj . rozmiar = rozmiar ; // Ustawienie właściwości foo . imię = imię ; // Metoda ustawiająca właściwość return foo ; }
C++
C++ nie ma ani UAP, ani właściwości, gdy obiekt jest zmieniany w taki sposób, że atrybut (kolor) staje się parą funkcji ( getA, setA ). Każde miejsce, które używa instancji obiektu i ustawia lub pobiera wartość atrybutu ( x = obj.color
lub obj.color = x
) musi zostać zmienione, aby wywołać jedną z funkcji. ( x = obj.getColor()
lub obj.setColor(x)
). Używanie szablonów i przeciążanie operatorów , możliwe jest sfałszowanie właściwości, ale jest to bardziej złożone niż w językach, które bezpośrednio obsługują właściwości. To komplikuje konserwację programów C++. Rozproszone biblioteki obiektów C++ muszą uważać na sposób, w jaki zapewniają dostęp do danych członkowskich.
JavaScript
JavaScript obsługuje obliczane właściwości od 2009 roku.