Późna odpowiedź, ale nie mogę się oprzeć.
Czy X w większości klas w Y jest dobry, czy jest anty-wzorem?
W większości przypadków większość zasad, zastosowanych bez zastanowienia, będzie w większości okropnie nie tak (w tym ta).
Pozwólcie, że opowiem wam historię o narodzinach obiektu w chaosie jakiegoś właściwego, szybkiego i brudnego, proceduralnego kodu, który wydarzył się nie z założenia, ale z desperacji.
Mój stażysta i ja programujemy w parach, aby szybko utworzyć wyrzucony kod do zeskrobania strony internetowej. Nie mamy absolutnie żadnego powodu, aby oczekiwać, że ten kod będzie trwał długo, więc po prostu staramy się stworzyć coś, co działa. Chwytamy całą stronę jako sznurek i kroimy potrzebne rzeczy w najbardziej niesamowity kruchy sposób, jaki można sobie wyobrazić. Nie osądzaj. To działa.
Teraz, robiąc to, stworzyłem statyczne metody rąbania. Mój stażysta stworzył klasę DTO, która była bardzo podobna do twojej CatData
.
Kiedy po raz pierwszy spojrzałem na DTO, to mnie wkurzyło. Lata szkód wyrządzonych przez Javę w moim mózgu spowodowały, że odrzuciłem się na publiczne tereny. Ale pracowaliśmy w C #. C # nie potrzebuje przedwczesnych programów pobierających i ustawiających, aby zachować prawo do uczynienia danych niezmiennymi lub enkapsulowanymi później. Bez zmiany interfejsu możesz je dodać w dowolnym momencie. Może po prostu możesz ustawić punkt przerwania. Wszystko bez informowania klientów o tym. Yea C #. Boo Java.
Więc trzymałem się za język. Patrzyłem, jak użył moich metod statycznych do zainicjowania tego przed użyciem. Mieliśmy około 14 z nich. To było brzydkie, ale nie mieliśmy powodu się przejmować.
Potem potrzebowaliśmy go w innych miejscach. Odkryliśmy, że chcemy skopiować i wkleić kod. Przerzucanych jest 14 linii inicjalizacji. Zaczynało boleć. Wahał się i poprosił mnie o pomysły.
Niechętnie zapytałem: „czy wziąłbyś pod uwagę przedmiot?”
Spojrzał ponownie na swój DTO i zmieszał twarz w dezorientacji. „To obiekt”.
„Mam na myśli prawdziwy przedmiot”
„Hę?”
„Pozwól, że coś ci pokażę. Ty decydujesz, czy to jest przydatne”
Wybrałem nową nazwę i szybko wymyśliłem coś, co wyglądało trochę tak:
public class Cat{
CatData(string catPage) {
this.catPage = catPage
}
private readonly string catPage;
public string name() { return chop("name prefix", "name suffix"); }
public string weight() { return chop("weight prefix", "weight suffix"); }
public string image() { return chop("image prefix", "image suffix"); }
private string chop(string prefix, string suffix) {
int start = catPage.indexOf(prefix) + prefix.Length;
int end = catPage.indexOf(suffix);
int length = end - start;
return catPage.Substring(start, length);
}
}
Nie zrobiło to nic, czego nie zrobiły już metody statyczne. Ale teraz wciągnąłem 14 metod statycznych do klasy, w której mogliby być sami z danymi, nad którymi pracowali.
Nie zmusiłem stażysty do korzystania z niego. Właśnie to zaoferowałem i pozwoliłem mu zdecydować, czy chce trzymać się metod statycznych. Poszedłem do domu, myśląc, że pewnie trzymałby się tego, co już pracował. Następnego dnia odkryłem, że używa go w wielu miejscach. Odszyfrował resztę kodu, który nadal był brzydki i proceduralny, ale ta odrobina złożoności była teraz ukryta przed nami za obiektem. Było trochę lepiej.
Teraz na pewno za każdym razem, gdy uzyskujesz do niego dostęp, robi sporo pracy. DTO jest ładną, szybko buforowaną wartością. Martwiłem się o to, ale zdałem sobie sprawę, że mogę dodać buforowanie, jeśli zajdzie taka potrzeba, bez dotykania żadnego z używanych kodów. Więc nie zamierzam zawracać sobie głowy, dopóki nas to nie obchodzi.
Czy mówię, że zawsze powinieneś trzymać się obiektów OO nad DTO? Nie. DTO błyszczy, gdy trzeba przekroczyć granicę, która powstrzymuje cię przed przemieszczaniem się metod. DTO mają swoje miejsce.
Ale podobnie jak obiekty OO. Dowiedz się, jak korzystać z obu narzędzi. Dowiedz się, ile kosztuje każda z nich. Naucz się decydować o problemie, sytuacji i stażu. Dogma nie jest tu twoim przyjacielem.
Ponieważ moja odpowiedź jest już absurdalnie długa, pozwólcie, że zwrócę uwagę na niektóre nieporozumienia związane z przeglądem waszego kodu.
Na przykład klasa zwykle ma członków klasy i metody, np .:
public class Cat{
private String name;
private int weight;
private Image image;
public void printInfo(){
System.out.println("Name:"+this.name+",weight:"+this.weight);
}
public void draw(){
//some draw code which uses this.image
}
}
Gdzie jest twój konstruktor? To nie pokazuje mi wystarczająco dużo, aby wiedzieć, czy jest to przydatne.
Ale po przeczytaniu na temat zasady pojedynczej odpowiedzialności i zasady otwartej zamkniętej wolę rozdzielić klasę na klasę DTO i klasę pomocniczą tylko metodami statycznymi, np .:
public class CatData{
public String name;
public int weight;
public Image image;
}
public class CatMethods{
public static void printInfo(Cat cat){
System.out.println("Name:"+cat.name+",weight:"+cat.weight);
}
public static void draw(Cat cat){
//some draw code which uses cat.image
}
}
Myślę, że to pasuje do zasady pojedynczej odpowiedzialności, ponieważ teraz CatData jest odpowiedzialna tylko za przechowywanie danych, nie przejmuje się metodami (także CatMethods).
Możesz zrobić wiele głupich rzeczy w imię zasady pojedynczej odpowiedzialności. Mógłbym argumentować, że Cat Strings i Cat ints powinny być oddzielone. Te metody rysowania i obrazy muszą mieć własną klasę. Że twój program działa jako jedna odpowiedzialność, więc powinieneś mieć tylko jedną klasę. : P
Dla mnie najlepszym sposobem na przestrzeganie zasady pojedynczej odpowiedzialności jest znalezienie dobrej abstrakcji, która pozwoli Ci zawrzeć w pudełku złożoność, abyś mógł ją ukryć. Jeśli potrafisz nadać mu dobre imię, dzięki któremu ludzie nie będą zaskoczeni tym, co znajdą, gdy zaglądają do środka, to dość dobrze go przestrzegasz. Oczekiwanie, że podyktuje to więcej decyzji, to będzie kłopot. Szczerze mówiąc, oba twoje kody robią to, więc nie rozumiem, dlaczego SRP ma tutaj znaczenie.
Jest to również zgodne z zasadą otwartego zamkniętego, ponieważ dodawanie nowych metod nie musi zmieniać klasy CatData.
Więc nie. Zasada otwartego zamknięcia nie polega na dodawaniu nowych metod. Chodzi o to, aby móc zmienić implementację starych metod i niczego nie edytować. Nic, co cię wykorzystuje, a nie twoje stare metody. Zamiast tego piszesz nowy kod gdzieś indziej. Jakaś forma polimorfizmu dobrze to zrobi. Nie widzę tego tutaj.
Moje pytanie brzmi, czy jest to dobry czy anty-wzór?
Do diabła, skąd mam wiedzieć? Spójrz, robienie tego w obie strony ma zalety i koszty. Po oddzieleniu kodu od danych możesz je zmienić bez konieczności ponownej kompilacji drugiego. Może jest to dla ciebie niezwykle ważne. Może to po prostu komplikuje twój kod.
Jeśli dzięki temu poczujesz się lepiej, nie jesteś tak daleko od czegoś, co Martin Fowler nazywa obiektem parametru . Nie musisz brać tylko prymitywów do swojego obiektu.
To, co chciałbym, abyś zrobił, to rozwinięcie poczucia, jak dokonać separacji, czy nie, w jednym ze stylów kodowania. Ponieważ wierz w to lub nie, nie musisz wybierać stylu. Musisz po prostu żyć z wyborem.