Czy zmiana nazwy metody pozwala zachować enkapsulację?

9

Czytałem tę stronę o tym , kiedy getters / setters są uzasadnieni, a OP podał następujący przykładowy kod:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

W Zaakceptowanych odpowiedź stany:

Nawiasem mówiąc, w swoim przykładzie, dałbym klasę i metod, zamiast i . Wtedy nadal miałbyś enkapsulację.FridgeputCheese()takeCheese()get_cheese()set_cheese()

Jak zachowuje się enkapsulację, zmieniając jej nazwę z get / set na putCheese()/ takeCheese()Oczywiście otrzymujesz / ustawiasz wartość, więc dlaczego nie po prostu zostawić ją jako get / set?

W tej samej odpowiedzi stwierdza również:

Posiadanie getterów i seterów samo w sobie nie przerywa enkapsulacji. To, co przerywa enkapsulację, to automatyczne dodawanie gettera i settera dla każdego członka danych (każdego pola, w języku Java), bez zastanowienia.

W tym przypadku mamy tylko jedną zmienną, cheesei możesz chcieć wziąć i zwrócić ser do lodówki, więc w takim przypadku para get / set jest uzasadniona.

QueenSvetlana
źródło
7
putCheesedodaje ser do lodówki i takeCheeseusuwa go - są to abstrakcje zorientowane na domenę (wyższy poziom), a nie obiekty pobierające i ustawiające pola obiektowe (które są (niskopoziomowymi) abstrakcjami programowania komputerowego).
Erik Eidt,

Odpowiedzi:

17

Myślę, że nie rozumiesz sedna sprawy. To nie znaczy, że powinieneś zmienić nazwę setera i gettera, ale mieć metody, które dodają i usuwają przedmioty z lodówki. to znaczy

public class Fridge
{
    private int numberOfCheeseSlices;

    public void AddCheeseSlices(int n)
    {
         if(n < 0) { 
             throw new Exception("you cant add negative cheese!");
         }
         if(numberOfCheeseSlices + n > this.capacityOfFridge) { 
             throw new Exception("TOO MUCH CHEESE!!");
         }
         ..etc
         this.numberOfCheeseSlices += n;
    }
}

Teraz zmienna prywatna jest enkapsulowana. Nie mogę po prostu ustawić tego, co chcę, mogę tylko dodawać i usuwać plastry sera z lodówki za pomocą metod, które z kolei zapewniają, że zastosowane zostaną wszelkie zasady logiki biznesowej dotyczące sera.

Ewan
źródło
2
Zgadza się, ale nadal możesz go zostawić, ponieważ setCheese()ma on taką samą logikę w ustawieniach. Dla mnie i tak próbujesz ukryć fakt, że używasz setera, zmieniając nazwę metody, ale oczywiście twój getter / ustawienie czegoś.
QueenSvetlana,
21
@QueenSvetlana to nie jest seter. To operacja. Mówisz lodówce, że „oto kolejny ser”, i dodaje ją do swojej wewnętrznej, zamkniętej liczby serów. Seter powiedziałby „masz teraz 5 serów”. Są to funkcjonalnie różne operacje, a nie tylko różne nazwy.
Ant P
1
można to nazwać setCheese, ale byłoby to mylące, ponieważ nie ustawiłoby wartości sera.
Ewan
6
Zauważ, że występuje tutaj błąd przepełnienia liczb całkowitych. Jeśli jest już więcej niż 0 serów, AddCheese(Integer.MAX_VALUE)powinno zawieść, ale nie.
user253751,
3
byłoby to w całości omówione w sekcji ..etc
Ewan
5

Gettery i settery przerywają enkapsulację za każdym razem , z definicji. Co może argumentować, że czasami musimy zrobić. W tym miejscu są moje odpowiedzi:

Jak zachowuje się enkapsulację, zmieniając jej nazwę z get / set na putCheese () / takeCheese () Oczywiście otrzymujesz / ustawiasz wartość, więc dlaczego nie po prostu zostaw ją jako get / set?

Różnica polega na semantyce, czyli na znaczeniu tego, co piszesz. W enkapsulacji chodzi nie tylko o ochronę, ale o ukrywanie stanu wewnętrznego. Stan wewnętrzny nie powinien być nawet znany na zewnątrz, zamiast tego obiekt powinien oferować metody biznesowe (czasami określane jako „zachowanie”) do manipulowania / używania tego stanu.

Tak geti setsą terminy techniczne i wydają się mieć nic wspólnego z „lodówką” domeny, podczas puti takemoże faktycznie jakiś sens.

Jednak , czy putlub takeuczynić rzeczywisty sens nadal zależy od wymagań i nie mogą być obiektywnie ocenione. Zobacz następny punkt:

W tym przypadku mamy tylko jedną zmienną, ser, i możesz chcieć wziąć i zwrócić ser do lodówki, więc para get / set w tym przypadku jest uzasadniona.

Nie można tego obiektywnie ustalić bez większego kontekstu. Twój przykład zawiera go_shopping()metodę gdzie indziej. Jeśli po to Fridgejest, to nie potrzebuje getlub setto, czego potrzebuje Fridge.go_shopping(). W ten sposób masz metodę wyprowadzoną z wymagań, wszystkie „dane”, których potrzebuje, są lokalne i masz rzeczywiste zachowanie zamiast tylko cienko zawoalowanej struktury danych.

Pamiętaj, że nie budujesz ogólnego, wielokrotnego użytku Fridge. Budujesz tylko Fridgedla swoich wymagań. Wszelkie wysiłki włożone w zarabianie na tym, co trzeba, są w rzeczywistości marnotrawstwem.

Robert Bräutigam
źródło
10
Getters and setters break encapsulation every single time- To trochę zbyt mocno sformułowane na mój gust. Metody (w tym gettery i setery) mają ten sam cel, co bramki na koncercie: umożliwiają uporządkowane wejście i wyjście z miejsca.
Robert Harvey
8
„Gettery i settery przerywają enkapsulację za każdym razem, z definicji” - według której definicji? Ja również mam z tym problem, ponieważ pobierające i ustawiające są po prostu częścią publicznego interfejsu, jak każdy inny członek publiczny; przerywają enkapsulację tylko wtedy, gdy ujawniają szczegóły implementacji, które z założenia są uznawane za wewnętrzne (tj. na podstawie decyzji programisty (lub zespołu)). Fakt, że osoby pobierające i ustawiające są często dodawane przypadkowo, a kontrola sprzężenia jest późniejsza, to zupełnie inna sprawa.
Filip Milovanović,
2
@ FilipMilovanović Uczciwy punkt. Jak wspomniałem w odpowiedzi, „enkapsulacja” służy do ukrywania wewnętrznych obiektów (== stan obiektu == zmienne instancji). Gettery i settery publikują wewnętrzne obiekty. Dlatego przez te definicje są w ciągłym konflikcie. Teraz, czy czasami trzeba złamać hermetyzacja jest inna sprawa, która powinna być traktowane oddzielnie. Mówiąc jasno, mówiąc, że osoby ustawiające / pobierające zawsze przerywają enkapsulację, nie mówię, że zawsze jest to „złe”, jeśli to pomaga.
Robert Bräutigam,
5
gettery i setery nie „przerywają enkapsulacji dosłownie zawsze”. Operatorzy pobierający i ustawiający często nawet nie odnoszą się bezpośrednio do członków pod spodem, wykonują jakąś operację lub są zdefiniowani wyłącznie, możesz mieć tylko gettera lub tylko setera. Przerywa enkapsulację, gdy pobierający i ustawiający nie robią nic więcej niż dostęp do elementu i oba są zdefiniowane dla tych samych pól. Nawet to może być konieczne dla opiekunów bibliotek, którzy nie chcą łamać ABI / API z wewnętrznymi zmianami i unikać ponownej kompilacji klienta.
kiedy
4
Ok, getters i setter tylko przerywają enkapsulację 99% czasu. Szczęśliwy? I, ujawniając typ zamkniętego obiektu, zdecydowanie przerywają enkapsulację. Gdy już public int getFoo()go masz, praktycznie niemożliwe jest przejście na inny typ.
user949300,
3

Prawie wszystko to pokazuje fundamentalne nieporozumienie dotyczące enkapsulacji i jej zastosowania.

Początkowa odpowiedź, że łamałeś enkapsulację, jest po prostu zła. Twoja aplikacja może wymagać jedynie ustawienia wartości sera w lodówce zamiast zwiększania / zmniejszania lub dodawania / usuwania. Ponadto nie jest to semantyka, bez względu na to, jak ją nazwiesz, jeśli musisz uzyskać dostęp i / lub zmienić atrybuty, nie przerywasz enkapsulacji, zapewniając je. Wreszcie, enkapsulacja tak naprawdę nie polega na „ukrywaniu się”, ale na kontrolowaniu dostępu do stanu i wartości, które nie powinny być publiczne ani manipulowane poza klasą, przy jednoczesnym przyznaniu go tym, którzy powinni, i wykonaniu zadania udostępnionego wewnętrznie.

Narzędzie pobierające lub ustawiające nie przerywa enkapsulacji, gdy istnieje uzasadniona potrzeba uzyskania lub ustawienia wartości. Właśnie dlatego metody można upublicznić.

Hermetyzacja polega na przechowywaniu danych i metodach, które modyfikują te dane bezpośrednio razem w jednym logicznym miejscu, klasie.

W tym szczególnym przypadku istnieje wyraźna potrzeba zmiany wartości sera we wniosku. Niezależnie od tego, jak to się robi, poprzez get / set lub add / remove, o ile metody są zawarte w klasie, postępujesz zgodnie ze stylem obiektowym.

Dla wyjaśnienia podam przykład złamania enkapsulacji poprzez zapewnienie dostępu bez względu na nazwę metody lub logiczne wykonanie.

Powiedzmy, że lodówka ma „żywotność”, wystarczy kilka tyknięć, zanim lodówka przestanie działać (ze względu na argument, lodówki nie można naprawić). Logicznie rzecz biorąc, nie ma możliwości, aby użytkownik (lub reszta aplikacji) mógł zmienić tę wartość. To powinno być prywatne. Byłoby to widoczne tylko poprzez powiedzenie innego publicznego atrybutu znanego jako „isWorking”. Po upływie okresu żywotności, wewnętrzne zestawy lodówek działają na fałsz.

Odliczanie czasu życia i przestawianie przełącznika isWorking jest wewnętrzne dla lodówki, nic na zewnątrz nie może / powinno mieć wpływu na proces. isWorking powinien być widoczny tylko dlatego getter nie przerywa enkapsulacji. Jednak dodanie akcesoriów dla elementów całego cyklu życia spowodowałoby uszkodzenie enkapsulacji.

Jak większość rzeczy, definicja enkapsulacji nie jest dosłowna, jest względna. Czy powinieneś widzieć X poza klasą? Czy powinieneś być w stanie zmienić Y? Czy wszystko, co dotyczy twojego obiektu tutaj w tej klasie, czy też funkcjonalność jest rozłożona na wiele klas?

użytkownik343330
źródło
Spójrzmy na to pragmatycznie. Mówisz, że enkapsulacja nie jest zepsuta, jeśli kod jest przeznaczony w ten sposób lub jeśli istnieje „uzasadniony” powód. Zasadniczo oznacza to, że nigdy nie możemy powiedzieć, że enkapsulacja jest zepsuta, ponieważ deweloper musi tylko powiedzieć, że „zamierzała” w ten sposób, lub że był „uzasadniony” powód. Więc ta definicja jest w zasadzie bezużyteczna, prawda?
Robert Bräutigam
Nie, mówię, że enkapsulacja nie jest zepsuta po prostu przez zapewnienie dostępu. I nie ma to nic wspólnego z tym, co mówi programista, ale jakie są potrzeby aplikacji. Aplikacja musi mieć dostęp do manipulowania obiektami, hermetyzacja polega na kontrolowaniu dostępu. Aplikacja może wymagać „ustawienia” atrybutu obiektu, ale logika i / lub obliczenia wymagane do wykonania tej operacji „ustawiania” powinny być zawarte w klasie obiektów.
user343330,
Myślę, że w tym zasadniczo się różnimy, mówicie: „Aplikacja może wymagać„ ustawienia ”atrybutu obiektu ...”. Nie wydaje mi się Wybór projektu obejmującego ustawienie lub uzyskanie atrybutu należy do programisty. To wybór! Istnieje wiele sposobów kodowania czegoś, nie wszystkie wymagają pobierania lub ustawiania wartości zmiennych instancji.
Robert Bräutigam
Może być ... Zwykle projektowanie opiera się na zaspokajaniu potrzeb, niezależnie od tego, czy programista wybierze jedno z nich, czy też środowisko biznesowe. W obu przypadkach, jeśli aplikacja ma podstawową potrzebę manipulowania wartością, która jest częścią obiektu, musisz udostępnić tę funkcję w jakiś sposób. Zapewnienie punktu wejścia do pracy nie przerywa enkapsulacji. Weź aplikację sprzedażową. W pewnym momencie Twoja transakcja powinna obliczyć podatek, robiąc to w obiekcie transakcji, byłby zamknięty, ale aplikacja powinna mieć możliwość podania stanu transakcji.
CalcTax
Setery są złymi przykładami ze względu na ich prostotę, pomyśl w kategoriach funkcji, która wykonuje o wiele więcej pracy, co powoduje aktualizację atrybutu obiektu w porównaniu do czegoś poza klasą, która wykonuje tę pracę, a następnie mówi object.attribute = x. Pierwszy z nich to dobra enkapsulacja, zapewniając odpowiedni dostęp, drugi nie. Jeśli chodzi o metody pobierające, czy aplikacja musi znać tylko ten fragment obiektu, aby zrobić coś innego, czego nie można zawrzeć w klasie obiektów? jeśli tak, ujawnienie go nie złamie enkapsulacji. jeśli nie, to
umieść
1

To nie tylko zmiana nazwy metody. Te dwie metody działają inaczej.

(Wyobraź to sobie w pamięci)

get_cheese i set_cheese eksponują ser. putCheese () i takeCheese () ukrywają ser, dbają o zarządzanie nim i dają użytkownikowi sposób na jego obsługę. Obserwator nie widzi sera, widzi tylko dwie metody manipulowania nim.

Daneesha
źródło