Po pierwsze, wyjaśnijmy paradygmat.
- Struktury danych -> układ pamięci, po którym można przemieszczać się i manipulować przy pomocy odpowiednio poinformowanych funkcji.
- Obiekty -> samodzielny moduł, który ukrywa swoją implementację i zapewnia interfejs, przez który można się komunikować.
Gdzie przydatny jest getter / setter?
Czy metody pobierające / ustawiające są przydatne w strukturach danych? Nie.
Struktura danych to specyfikacja układu pamięci, która jest wspólna i manipulowana przez rodzinę funkcji.
Zasadniczo każda stara nowa funkcja może nadejść i manipulować strukturą danych, jeśli robi to w taki sposób, że inne funkcje nadal ją rozumieją, wówczas funkcja dołącza do rodziny. W przeciwnym razie jest to nieuczciwa funkcja i źródło błędów.
Nie zrozumcie mnie źle, że może istnieć kilka rodzin funkcji walczących o tę strukturę danych ze snitchem, płaszczem i podwójnym agentem wszędzie. To dobrze, gdy każdy z nich ma własną strukturę danych, z którą można się bawić, ale kiedy się nią dzielą ... wyobraź sobie, że kilka rodzin przestępczych nie zgadza się z polityką, może stać się naprawdę szybko bałaganem.
Biorąc pod uwagę bałagan, jaki rodziny rozszerzonych funkcji mogą osiągnąć, czy istnieje sposób na zakodowanie struktury danych, aby nieuczciwe funkcje nie zepsuły wszystkiego? Tak, nazywają się Obiektami.
Czy gettery / settery są przydatne w Objectach? Nie.
Cały sens zawijania struktury danych w obiekcie polegał na tym, aby nie istniały żadne nieuczciwe funkcje. Jeśli funkcja chciała dołączyć do rodziny, najpierw musiała zostać dokładnie sprawdzona, a następnie stać się częścią obiektu.
Punktem / celem gettera i setera jest umożliwienie funkcjom poza obiektem bezpośredniej zmiany układu pamięci obiektu. To brzmi jak otwarte drzwi dla łotrzyków ...
The Edge Case
Istnieją dwie sytuacje, w których publiczny getter / setter ma sens.
- Część struktury danych w obiekcie jest zarządzana przez obiekt, ale nie jest kontrolowana przez obiekt.
- Interfejs opisujący abstrakcyjną strukturę danych na wysokim poziomie, w której niektóre elementy nie powinny mieć kontroli nad obiektem implementującym.
Kontenery i interfejsy kontenerów są idealnymi przykładami obu tych dwóch sytuacji. Kontener wewnętrznie zarządza strukturami danych (lista połączona, mapa, drzewo), ale zapewnia kontrolę nad określonym elementem wszystkim i różnym. Interfejs to streszcza i całkowicie ignoruje implementację i opisuje tylko oczekiwania.
Niestety wiele implementacji źle to rozumie i definiuje interfejs tego rodzaju obiektów, aby zapewnić bezpośredni dostęp do rzeczywistego obiektu. Coś jak:
interface Container<T>
{
typedef ...T... TRef; //<somehow make TRef to be a reference or pointer to the memory location of T
TRef item(int index);
}
To jest zepsute. Wdrożenia kontenera muszą jawnie przekazać kontrolę nad swoimi elementami wewnętrznymi komukolwiek, kto ich używa. Nie widziałem jeszcze języka o zmiennej wartości, w którym jest to w porządku (języki z semantyką wartości niezmiennej są z definicji w porządku z perspektywy niszczenia danych, ale niekoniecznie z perspektywy szpiegowania danych).
Możesz poprawić / poprawić gettery / setter, używając tylko semantyki kopiowania lub używając proxy:
interface Proxy<T>
{
operator T(); //<returns a copy
... operator ->(); //<permits a function call to be forwarded to an element
Proxy<T> operator=(T); //< permits the specific element to be replaced/assigned by another T.
}
interface Container<T>
{
Proxy<T> item(int index);
T item(int index); //<When T is a copy of the original value.
void item(int index, T new_value); //<where new_value is used to replace the old value
}
Prawdopodobnie nieuczciwa funkcja mogłaby nadal odgrywać chaos (przy wystarczającym wysiłku większość rzeczy jest możliwa), ale semantyka kopiowania i / lub proxy zmniejsza prawdopodobieństwo wystąpienia wielu błędów.
- przelewowy
- niedomiar
- interakcje z elementem podrzędnym są sprawdzane pod kątem typu / sprawdzalne pod względem typu (w typie tracenia języków jest to dobrodziejstwo)
- Rzeczywisty element może, ale nie musi, być rezydentem pamięci.
Osoby pobierające / ustawiające prywatne
To ostatni bastion pobierających i ustawiających pracujących bezpośrednio na tym typie. W rzeczywistości nie nazwałbym nawet tych pobierających i ustawiających, ale akcesoria i manipulatory.
W tym kontekście czasami manipulowanie określoną częścią struktury danych zawsze / prawie zawsze / ogólnie wymaga określonego przechowywania ksiąg. Powiedzmy, że podczas aktualizacji katalogu głównego drzewa pamięć podręczna musi zostać wyczyszczona lub podczas uzyskiwania dostępu do zewnętrznego elementu danych należy uzyskać / zwolnić blokadę. W takich przypadkach sensowne jest zastosowanie zasady SUCHEJ i spakowanie tych działań razem.
W kontekście prywatnym nadal możliwe jest, aby inne funkcje w rodzinie pomijały te „moduły pobierające i ustawiające” oraz manipulowały strukturą danych. Dlatego myślę o nich bardziej jako o akcesoriach i manipulatorach. Możesz uzyskać bezpośredni dostęp do danych lub polegać na innym członku rodziny, aby uzyskać właściwą część.
Chronieni Getters / Setters
W kontekście chronionym nie różni się tak bardzo od kontekstu publicznego. Zagraniczne, prawdopodobnie nieuczciwe funkcje chcą dostępu do struktury danych. Więc nie, jeśli istnieją, działają jak publiczne podmioty pobierające / ustawiające.
this->variable = x + 5
lub wywołaćUpdateStatistics
funkcję settera, w takich przypadkachclassinstancea->variable = 5
spowoduje to problemy.