Przeglądając napisany przeze mnie kod, natknąłem się na następującą konstrukcję, która skłoniła mnie do myślenia. Na pierwszy rzut oka wydaje się wystarczająco czysty. Tak, w rzeczywistym kodzie getLocation()
metoda ma nieco bardziej konkretną nazwę, która lepiej opisuje dokładnie, jaką lokalizację otrzymuje.
service.setLocation(this.configuration.getLocation().toString());
W tym przypadku service
jest to zmienna instancji znanego typu, zadeklarowana w ramach metody. this.configuration
pochodzi z przekazania do konstruktora klasy i jest instancją klasy implementującej określony interfejs (która nakazuje getLocation()
metodę publiczną ). Dlatego this.configuration.getLocation()
znany jest typ zwracanego wyrażenia ; szczególnie w tym przypadku jest to java.net.URL
, podczas gdy service.setLocation()
chce String
. Ponieważ oba typy String i URL nie są bezpośrednio kompatybilne, wymagana jest pewna konwersja, aby dopasować kwadratowy kołek do okrągłego otworu.
Jednakże , zgodnie z prawem Demetry jak cytowane w czystego kodu , metoda A F w klasie C powinien wywołać tylko metody, na C , przedmiotów wytworzonych lub przekazywane jako argumenty f i obiektów odbywa się w zmiennych instancji C . Cokolwiek poza tym (ostatni toString()
w moim szczególnym przypadku powyżej, chyba że weźmiesz pod uwagę obiekt tymczasowy stworzony w wyniku samej inwokacji metody, w którym to przypadku całe Prawo wydaje się być dyskusyjne) jest niedozwolone.
Czy istnieje uzasadnione powód, dla którego połączenie takie jak powyższe, biorąc pod uwagę wymienione ograniczenia, powinno być zniechęcone, a nawet zabronione? A może po prostu jestem zbyt złośliwy?
Gdybym wdrożyć metodę URLToString()
, która po prostu wywołuje toString()
na URL
obiekt (taki jak ten zwrócony przez getLocation()
), przekazanym mu jako parametr i zwraca wynik, mogę zawinąć getLocation()
wywołanie w to, aby osiągnąć dokładnie ten sam wynik; skutecznie przesunąłbym konwersję o jeden krok na zewnątrz. Czy to w jakiś sposób sprawi, że będzie to do przyjęcia? ( Wydaje mi się intuicyjnie, że nie powinno to robić żadnej różnicy, ponieważ wszystko to trochę przesuwa. Jednak, zgodnie z cytowaną literą Prawa Demeter, byłoby to do przyjęcia, ponieważ ja wtedy działałby bezpośrednio na parametrze funkcji).
Czy miałoby to jakąkolwiek różnicę, gdyby chodziło o coś nieco bardziej egzotycznego niż wywołanie toString()
standardowego typu?
Odpowiadając, pamiętaj, że zmiana zachowania lub interfejsu API typu, którego service
jest zmienna, nie jest praktyczna. Ponadto, ze względu na argument, powiedzmy, że zmiana typu zwracanego getLocation()
jest również niepraktyczna.
Prawo Demeter jest wytyczną projektową, a nie prawem, którego należy przestrzegać religijnie.
Jeśli uważasz, że twoje klasy są wystarczająco oddzielone, to nie ma nic złego w linii,
this.configuration.getLocation()
szczególnie jeśli, jak mówisz, zmiana innych części interfejsu API jest niepraktyczna.Jestem prawie pewien, że klient będzie całkowicie szczęśliwy, nawet jeśli rozłożysz Prawo Demeter na kawałki, o ile tylko dostarczysz to, czego chce i na czas. Co nie jest usprawiedliwieniem tworzenia złego oprogramowania, ale przypomnieniem, aby być pragmatycznym podczas opracowywania oprogramowania.
źródło
this.configuration
jest to zmienna instancji typu znanego interfejsu, wywołanie na niej metody zdefiniowanej przez ten interfejs wydaje się w porządku, nawet przy ścisłej interpretacji. Tak, wiem, że jest to wytyczna, podobnie jak KISS, SOLID, YAGNI i tak dalej. Istnieje bardzo niewiele, jeśli w ogóle, jakichkolwiek „praw” (w sensie prawnym) w ogólnym rozwoju oprogramowania.Jedyne, co mogę wymyślić, aby nie pisać takiego kodu, to co, jeśli
this.configuration.getLocation()
zwróci wartość null? To zależy od otaczającego kodu i docelowych odbiorców korzystających z tego kodu. Ale, jak mówi Marco - prawo Demetera jest ogólną zasadą - dobrze jest postępować, ale nie łam się plecom, robiąc to niepotrzebnie.źródło
this.configuration.getLocation()
zwróci wartość null, wówczas (a) najprawdopodobniej nigdy nie zaszlibyśmy tak daleko lub (b) wydarzyło się coś katastroficznego w międzyczasie, w którym to przypadku chciałbym, aby kod zawiódł. Chociaż jest to z pewnością słuszny punkt, w tym konkretnym przypadku dość bezpiecznie jest powiedzieć, że nie ma zastosowania. Ponadto, wszystko to i wiele więcej to moduł obsługi wyjątków zaprojektowany specjalnie do radzenia sobie z tego rodzaju nieoczekiwanymi awariami.Ściśle przestrzeganie prawa Demetera oznaczałoby, że powinieneś zaimplementować metodę w obiekcie konfiguracyjnym, takim jak:
ale osobiście nie zawracałbym sobie głowy, ponieważ jest to tak mała sytuacja, a poza tym twoje ręce są trochę związane z powodu interfejsu API, którego nie możesz zmienić. Reguły programowania dotyczą tego, co powinieneś zrobić, kiedy masz wybór, ale czasami nie masz wyboru.
źródło