Nie sądzę, aby istniała odpowiedź na to pytanie niezależnie od języka, ponieważ to, co stanowi „właściwość”, jest pytaniem specyficznym dla danego języka, a to, czego oczekuje osoba wywołująca „właściwość”, jest również pytaniem specyficznym dla danego języka. Myślę, że najbardziej owocnym sposobem na myślenie o tym jest myślenie o tym, jak to wygląda z punktu widzenia dzwoniącego.
W języku C # właściwości wyróżniają się tym, że są (konwencjonalnie) pisane wielkimi literami (jak metody), ale nie zawierają nawiasów (jak zmienne instancji publicznej). Jeśli widzisz następujący kod, brak dokumentacji, czego oczekujesz?
var reciprocalHeading = myHeading.Reciprocal;
Jako względny nowicjusz w języku C #, ale czytający Wytyczne użytkowania nieruchomości firmy Microsoft , spodziewałbym Reciprocal
się między innymi:
- być logicznym członkiem
Heading
klasy
- bądź niedrogi, aby zadzwonić, aby nie trzeba było buforować wartości
- brak obserwowalnych skutków ubocznych
- dają ten sam wynik, jeśli zostaną wywołane dwa razy pod rząd
- (może) zaoferować
ReciprocalChanged
wydarzenie
Spośród tych założeń (3) i (4) są prawdopodobnie poprawne (zakładając, że Heading
jest to niezmienny typ wartości, jak w odpowiedzi Ewana ), (1) jest dyskusyjny, (2) jest nieznany, ale także dyskusyjny, a (5) jest mało prawdopodobne mają sens semantyczny (chociaż cokolwiek ma nagłówek, może być HeadingChanged
zdarzeniem). To sugeruje mi, że w API C # „pobierz lub oblicz wzajemność” nie powinno być implementowane jako właściwość, ale zwłaszcza jeśli obliczenia są tanie i Heading
są niezmienne, jest to przypadek graniczny.
(Zauważ jednak, że żaden z tych problemów nie ma nic wspólnego z tym, czy wywołanie właściwości tworzy nową instancję , nawet (2). Tworzenie obiektów w CLR samo w sobie jest dość tanie).
W Javie właściwości są konwencją nazewnictwa metod. Jeśli widzę
Heading reciprocalHeading = myHeading.getReciprocal();
moje oczekiwania są podobne do powyższych (jeśli mniej wyraźnie określone): oczekuję, że połączenie będzie tanie, idempotentne i pozbawione skutków ubocznych. Jednak poza ramą JavaBeans koncepcja „właściwości” nie jest tak bardzo znacząca w Javie, a zwłaszcza, gdy rozważa się niezmienną właściwość bez odpowiadającej jej setReciprocal()
, getXXX()
konwencja jest obecnie nieco staromodna. Od Effective Java , drugie wydanie (już ponad osiem lat):
Metody zwracające boolean
funkcję lub atrybut obiektu, na który są wywoływane, są zwykle nazywane rzeczownikiem, wyrażeniem rzeczownikowym lub wyrażeniem czasownikowym rozpoczynającym się od czasownika get
… Istnieje kontyngent wokalny, który twierdzi, że tylko trzecia forma (zaczynająca się od get
) jest akceptowalna, ale jest niewiele podstaw do tego twierdzenia. Pierwsze dwie formy zwykle prowadzą do bardziej czytelnego kodu… (s. 239)
Zatem we współczesnym, bardziej płynnym API spodziewałbym się zobaczyć
Heading reciprocalHeading = myHeading.reciprocal();
- co ponownie sugeruje, że wywołanie jest tanie, idempotentne i nie ma skutków ubocznych, ale nie powiedziałoby nic o tym, czy wykonywane jest nowe obliczenie, czy tworzony jest nowy obiekt. To jest w porządku; w dobrym API, nie powinno mnie to obchodzić.
W Ruby nie ma czegoś takiego jak własność. Istnieją „atrybuty”, ale jeśli widzę
reciprocalHeading = my_heading.reciprocal
Nie mam bezpośredniego sposobu, aby dowiedzieć się, czy uzyskuję dostęp do zmiennej instancji @reciprocal
za attr_reader
pomocą prostej metody akcesorium, czy też wywołuję metodę, która wykonuje kosztowne obliczenia. Jednak fakt, że nazwa metody jest rzeczownikiem prostym, zamiast powiedzieć calcReciprocal
, sugeruje ponownie, że wywołanie jest co najmniej tanie i prawdopodobnie nie ma skutków ubocznych.
W Scali konwencja nazewnictwa mówi, że metody z efektami ubocznymi przyjmują nawiasy, a metody bez nich nie, ale
val reciprocal = heading.reciprocal
może być jednym z:
// immutable public value initialized at creation time
val reciprocal: Heading = …
// immutable public value initialized on first use
lazy val reciprocal: Heading = …
// public method, probably recalculating on each invocation
def reciprocal: Heading = …
// as above, with parentheses that, by convention, the caller
// should only omit if they know the method has no side effects
def reciprocal(): Heading = …
(Zauważ, że Scala dopuszcza różne rzeczy, które mimo wszystko są zniechęcane przez przewodnik po stylu . To jedna z moich głównych irytacji ze Scali.)
Brak nawiasów mówi mi, że połączenie nie ma skutków ubocznych; nazwa ponownie sugeruje, że połączenie powinno być stosunkowo tanie. Poza tym nie obchodzi mnie, w jaki sposób zyskuje mi wartość.
W skrócie: poznaj język, którego używasz, i dowiedz się, jakie oczekiwania przyniosą inni programiści w interfejsie API. Cała reszta to szczegół implementacji.
Heading
jako niezmiennego typu ireciprocal
powrót nowegoHeading
jest dobrą praktyką „dołu sukcesu”. (z zastrzeżeniem, że dwa wezwaniareciprocal
powinny zwrócić „to samo”, tzn. powinny przejść test równości.)