Czy rozdzielenie większości klas na klasy tylko pola danych i klasy tylko metody (jeśli to możliwe) jest dobrym czy anty-wzorcem?

10

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
    }
}

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). Jest to również zgodne z zasadą otwartego zamkniętego, ponieważ dodawanie nowych metod nie musi zmieniać klasy CatData.

Moje pytanie brzmi, czy jest to dobry czy anty-wzór?

ocomfd
źródło
15
Robienie czegokolwiek na ślepo, ponieważ nauczyłeś się nowej koncepcji, zawsze jest anty-wzorem.
Kayaman
4
Jaki byłby sens klas, gdyby zawsze lepiej było oddzielić dane od metod, które je modyfikują? To brzmi jak programowanie niezorientowane obiektowo (które z pewnością ma swoje miejsce). Ponieważ jest to oznaczone jako „obiektowe”, zakładam, że chcesz korzystać z narzędzi dostarczanych przez OOP?
user1118321
4
Kolejne pytanie dowodzące, że SRP jest tak źle źle zrozumiany, że należy go zbanować. Przechowywanie danych w wielu miejscach publicznych nie jest obowiązkiem .
user949300,
1
@ user949300, jeśli klasa jest odpowiedzialna za te pola, przeniesienie ich w inne miejsce w aplikacji będzie oczywiście miało zerowy wpływ. Innymi słowy, mylisz się: w 100% to odpowiedzialność.
David Arno
2
@DavidArno i R Schmitz: Zawieranie pól nie jest traktowane jako odpowiedzialność do celów SRP. Gdyby tak było, nie byłoby OOP, ponieważ wszystko byłoby DTO, a następnie osobne klasy zawierałyby procedury do pracy z danymi. Jedna odpowiedzialność dotyczy jednego interesariusza . (Chociaż wydaje się, że zmienia się to nieznacznie w każdej iteracji zleceniodawcy)
user949300

Odpowiedzi:

10

Pokazałeś dwie skrajności („wszystko prywatne i wszystkie (być może niepowiązane) metody w jednym obiekcie” vs. „wszystko publiczne i żadna metoda wewnątrz obiektu”). IMHO dobre modelowanie OO nie jest żadnym z nich , najsłodsze miejsce jest gdzieś pośrodku.

Jednym lakmusowym testem tego, które metody lub logika należą do klasy, a co na zewnątrz, jest sprawdzenie zależności, które wprowadzą metody. Metody, które nie wprowadzają dodatkowych zależności są w porządku, o ile dobrze pasują do abstrakcji danego obiektu . Metody wymagające dodatkowych zewnętrznych zależności (takie jak biblioteka rysunków lub biblioteka we / wy) rzadko są dobrym rozwiązaniem. Nawet jeśli spowodujesz, że zależności znikną za pomocą wstrzykiwania zależności, nadal zastanowię się dwa razy, czy umieszczenie takich metod w klasie domeny jest naprawdę konieczne.

Więc nie powinieneś upubliczniać każdego członka ani nie musisz implementować metod dla każdej operacji na obiekcie w klasie. Oto alternatywna sugestia:

public class Cat{
    private String name;
    private int weight;
    private Image image;

    public String getInfo(){
        return "Name:"+this.name+",weight:"+this.weight;
    }
    public Image getImage(){
        return image;
    }
}

Teraz Catobiekt zapewnia wystarczającą logikę, aby otaczający kod mógł łatwo zaimplementować printInfoi drawbez ujawniania wszystkich atrybutów publicznie. Właściwe miejsce dla tych dwóch metod najprawdopodobniej nie jest klasą bogów CatMethods(ponieważ printInfoi drawnajprawdopodobniej są to różne obawy, więc myślę, że jest bardzo mało prawdopodobne, aby należały do ​​tej samej klasy).

Mogę sobie wyobrazić, CatDrawingControllerktóry implementuje draw(i może używa wstrzykiwania zależności do uzyskania obiektu Canvas). Mogę również wyobrazić sobie inną klasę, która implementuje niektóre dane wyjściowe i zastosowania konsoli getInfo( printInfow tym kontekście mogą stać się przestarzałe). Ale aby podejmować rozsądne decyzje w tej sprawie, trzeba znać kontekst i sposób, w jaki Catklasa będzie faktycznie używana.

Tak właśnie interpretowałem krytyków Anemic Domain Model Fowlera - dla logiki generalnie wielokrotnego użytku (bez zewnętrznych zależności) same klasy domen są dobrym miejscem, więc powinny być do tego wykorzystywane. Ale to nie oznacza implementacji żadnej logiki, wręcz przeciwnie.

Zwróć też uwagę, że powyższy przykład pozostawia miejsce na podjęcie decyzji o (im) zmienności. Jeśli Catklasa nie ujawni żadnych ustawień i Imagejest niezmienna, ta konstrukcja pozwoli na Catniezmienne (czego nie zrobi podejście DTO). Ale jeśli uważasz, że niezmienność nie jest wymagana lub nie jest pomocna w twoim przypadku, możesz również pójść w tym kierunku.

Doktor Brown
źródło
Uruchom szczęśliwe downvoters, robi się nudno. Jeśli masz krytyków, daj mi znać. Z przyjemnością wyjaśnię wszelkie nieporozumienia, jeśli je masz.
Doc Brown
Chociaż ogólnie zgadzam się z twoją odpowiedzią ... Wydaje mi się, getInfo()że ktoś wprowadza w błąd, zadając to pytanie. Subtelnie łączy obowiązki związane z prezentacją printInfo(), pokonując cel DrawingControllerklasy
Adriano Repetti,
@AdrianoRepetti Nie sądzę, że to pokonuje cel DrawingController. Zgadzam się z tym getInfo()i printInfo()są okropnymi nazwiskami. Zastanów się toString()iprintTo(Stream stream)
candied_orange
@Candid nie chodzi tylko o nazwę, ale o to, co robi. Ten kod dotyczy tylko szczegółów prezentacji i skutecznie po prostu odebraliśmy dane wyjściowe, a nie przetworzoną treść i jej logikę.
Adriano Repetti
@AdrianoRepetti: szczerze mówiąc, to tylko wymyślony przykład, aby dopasować się do pierwotnego pytania i zademonstrować mój punkt widzenia. W prawdziwym systemie będzie otaczający kontekst, który oczywiście pozwoli na podejmowanie decyzji dotyczących lepszych metod i nazw.
Doc Brown
6

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.

candied_orange
źródło
2

Natknąłeś się na temat debaty, która od ponad dekady tworzy argumenty wśród programistów. W 2003 roku Martin Fowler ukuł frazę „Anemic Domain Model” (ADM), aby opisać to oddzielenie danych i funkcji. On - i inni, którzy się z nim zgadzają - twierdzą, że „Bogate modele domenowe” (łączenie danych i funkcjonalności) to „właściwe OO”, a podejście ADM to anty-wzorzec inny niż OO.

Zawsze byli tacy, którzy odrzucają ten argument, a ta strona argumentu stała się głośniejsza i odważniejsza w ostatnich latach wraz z przyjęciem przez większą liczbę twórców technik rozwoju funkcjonalnego. Takie podejście aktywnie zachęca do rozdzielenia danych i obaw dotyczących funkcji. Dane powinny być w możliwie największym stopniu niezmienne, tak więc enkapsulacja stanu zmiennego staje się nieistotna. W takich sytuacjach dołączanie funkcji bezpośrednio do tych danych nie przynosi korzyści. To, czy jest to wtedy „nie OO”, czy nie, absolutnie nie interesuje takich ludzi.

Bez względu na to, po której stronie ogrodzenia siedzicie (siedzę bardzo mocno na stronie „Martin Fowler mówi mnóstwo starych rzeczy” BTW), wasze użycie metod statycznych printInfoi drawjest niemal powszechnie odrzucone. Metody statyczne trudno kpić z pisania testów jednostkowych. Więc jeśli mają skutki uboczne (takie jak drukowanie lub rysowanie na jakimś ekranie lub innym urządzeniu), nie powinny być statyczne lub powinny mieć przekazaną lokalizację wyjściową jako parametr.

Możesz mieć interfejs:

public interface CatMethods {
    void printInfo(Cat cat);
    void draw(Cat cat);
}

I implementacja, która jest wstrzykiwana do reszty systemu w czasie wykonywania (z innymi implementacjami używanymi do testowania):

internal class CatMethodsForScreen implements CatMethods {
    public void printInfo(Cat cat) {
        System.out.println("Name:"+cat.name+",weight:"+cat.weight);
    }

    public void draw(Cat cat) {
        //some draw code which uses cat.image
    }
}

Lub dodaj dodatkowe parametry, aby usunąć skutki uboczne z tych metod:

public static class CatMethods {
    public static void printInfo(Cat cat, OutputHandler output) {
        output.println("Name:"+cat.name+",weight:"+cat.weight);
    }

    public static void draw(Cat cat, Canvas canvas) {
        //some draw code which uses cat.image and draws it on canvas
    }
}
David Arno
źródło
Ale dlaczego miałbyś próbować przekształcić język OO, taki jak Java, w język funkcjonalny, zamiast od początku posługiwać się językiem funkcjonalnym? Czy to coś w rodzaju „Nienawidzę Javy, ale wszyscy jej używają, więc muszę, ale przynajmniej zamierzam napisać po swojemu”. Chyba że twierdzisz, że paradygmat OO jest przestarzały i w pewien sposób przestarzały. Zapisuję się do szkoły myślenia „Jeśli chcesz X, wiesz, gdzie go zdobyć”.
Kayaman
@Kayaman, „ Subskrybuję„ Jeśli chcesz X, wiesz, gdzie go zdobyć ”„ szkołą myślenia ”. Ja nie. Uważam tę postawę za krótkowzroczną i nieco obraźliwą. Nie używam Javy, ale używam C # i ignoruję niektóre funkcje „prawdziwego OO”. Nie dbam o dziedziczenie, obiekty stanowe itp. Piszę wiele metod statycznych i zapieczętowanych (końcowych) klas. Narzędzia i wielkość społeczności wokół C # nie ma sobie równych. Dla F # jest to o wiele gorsze. Tak więc subskrybuję szkołę myślenia „Jeśli chcesz X, poproś o dodanie go do twojego obecnego narzędzia”.
David Arno
1
Powiedziałbym, że ignorowanie aspektów języka różni się znacznie od próby miksowania i dopasowywania funkcji z innych języków. Nie próbuję też pisać „prawdziwego OO”, ponieważ po prostu przestrzeganie reguł (lub zasad) w niedokładnej nauce, takiej jak programowanie, nie działa. Oczywiście musisz znać zasady, aby je złamać. Ślepe wprowadzanie funkcji może być niekorzystne dla rozwoju języka. Bez obrazy, ale IMHO w części sceny FP wydaje się mieć wrażenie, że „FP jest najlepszą rzeczą od krojonego chleba, dlaczego ludzie tego nie rozumieją”. Nigdy bym nie powiedział (ani nie pomyślał), że OO lub Java są „najlepsze”.
Kayaman
1
@Kayaman, ale co to znaczy „próbować łączyć i dopasowywać funkcje z innych języków”? Czy argumentujesz, że funkcje Java nie powinny być dodawane do java, ponieważ „Java jest językiem OO”? Jeśli tak, to moim zdaniem jest to krótkowzroczny argument. Niech język ewoluuje wraz z potrzebami programistów. Wydaje mi się, że zmuszanie ich do przejścia na inny język jest niewłaściwym podejściem. Pewnie, że niektórzy „zwolennicy FP” robią wszystko, aby FP była najlepszą rzeczą na świecie. A niektórzy „zwolennicy OO” przesadzają, że OO „wygrał bitwę, ponieważ jest wyraźnie lepszy”. Zignoruj ​​ich obu.
David Arno,
1
Nie jestem przeciwnikiem FP. Używam go w Javie, gdzie jest to przydatne. Ale jeśli programista powie „Chcę tego”, to tak, jakby klient powiedział „Chcę tego”. Programiści nie są projektantami języków, a klienci nie są programistami. Poparłem twoją odpowiedź, ponieważ powiedziałeś, że „rozwiązanie” OP jest niezadowolone. Komentarz w pytaniu jest jednak bardziej zwięzły, tj. Wymyślił programowanie proceduralne w języku OO. Ogólny pomysł ma jednak sens, jak wykazałeś. Nieanemiczny model domeny nie oznacza, że ​​dane i zachowanie są wyłączne, a zewnętrzni rendererzy lub prezenterzy byliby zabronieni.
Kayaman
0

DTO - Obiekty transportu danych

są do tego przydatne. Jeśli zamierzasz przetaczać dane między programami lub systemami, pożądane są DTO, ponieważ zapewniają one zarządzalny podzbiór obiektu, który dotyczy tylko struktury danych i formatowania. Zaletą jest to, że nie trzeba synchronizować aktualizacji złożonych metod w kilku systemach (o ile dane podstawowe się nie zmieniają).

Cały sens OO polega na ścisłym powiązaniu danych i kodu działającego na te dane. Rozdzielanie obiektu logicznego na osobne klasy jest zwykle złym pomysłem.

James Anderson
źródło
-1

Wiele wzorców projektowych i zasad jest sprzecznych, a ostatecznie tylko Twoja ocena dobrego programisty pomoże ci zdecydować, które wzorce powinny dotyczyć konkretnego problemu.

Moim zdaniem zasada Zrób najprostszą rzecz, która może być możliwa, powinna wygrać domyślnie, chyba że masz silny powód, aby skomplikować projekt. W twoim konkretnym przykładzie wybrałbym pierwszy projekt, który jest bardziej tradycyjnym podejściem obiektowym z danymi i funkcjami razem w tej samej klasie.

Oto kilka pytań, które możesz sobie zadać na temat aplikacji:

  • Czy kiedykolwiek będę musiał podać alternatywny sposób renderowania obrazu Cat? Jeśli tak, czy dziedziczenie nie będzie dogodnym sposobem na zrobienie tego?
  • Czy moja klasa Cat musi dziedziczyć z innej klasy lub spełniać wymagania interfejsu?

Odpowiedź na którekolwiek z tych pytań może doprowadzić do bardziej złożonego projektu z osobnymi klasami DTO i funkcyjnymi.

Jordan Rieger
źródło
-1

Czy to dobry wzór?

Prawdopodobnie dostaniesz tutaj różne odpowiedzi, ponieważ ludzie podążają za różnymi szkołami myślenia. Zanim zacznę: odpowiedź ta jest zgodna z programowaniem obiektowym, tak jak to opisuje Robert C. Martin w książce „Clean Code”.


„True” OOP

O ile mi wiadomo, istnieją 2 interpretacje OOP. Dla większości ludzi sprowadza się to do „obiekt jest klasą”.

Jednak druga szkoła myślenia okazała się bardziej pomocna: „Obiekt jest czymś, co coś robi”. Jeśli pracujesz z klasami, każda klasa byłaby jedną z dwóch rzeczy: „ posiadaczem danych ” lub obiektem . Posiadacze danych przechowują dane (duh), obiekty robią różne rzeczy. To nie znaczy, że posiadacz danych nie może mieć metod! Pomyśl o list: A listtak naprawdę nic nie robi , ale ma metody, które wymuszają sposób przechowywania danych .

Posiadacze danych

Jesteś Catnajlepszym przykładem posiadacza danych. Jasne, poza twoim programem kot jest czymś, co coś robi , ale w twoim programie Catjest ciąg name, int weighti obraz image.

To, że posiadacze danych nic nie robią , nie oznacza również, że nie zawierają logiki biznesowej! Jeśli twoja Catklasa wymaga konstruktora name, weighta imagety pomyślnie sformułowałeś regułę biznesową, że każdy kot ma je. Jeśli możesz zmienić weightpo utworzeniu Catklasy, jest to kolejna enkapsulacja reguły biznesowej. To są bardzo podstawowe rzeczy, ale to tylko oznacza, że ​​bardzo ważne jest, aby nie popełnić błędu - w ten sposób zapewnisz, że nie da się tego źle pomylić.

Obiekty

Obiekty coś robią. Jeśli chcesz oczyścić obiekty *, możemy zmienić to na „Obiekty robią jedną rzecz”.

Drukowanie informacji o kocie jest zadaniem obiektu. Na przykład możesz nazwać ten obiekt „ CatInfoLogger” za pomocą metody publicznej Log(Cat). To wszystko, co musimy wiedzieć z zewnątrz: ten obiekt rejestruje informacje kota i aby to zrobić, potrzebuje Cat.

Wewnątrz ten obiekt będzie zawierał odniesienia do wszystkiego, czego potrzebuje, aby wypełnić swoją jedyną odpowiedzialność za rejestrowanie kotów. W tym przypadku, to tylko odniesienie do Systemza System.out.println, ale w większości przypadków będzie to prywatne odniesienia do innych obiektów. Jeśli logika formatowania wyjścia przed wydrukowaniem staje się zbyt skomplikowana, CatInfoLoggermożna po prostu uzyskać odniesienie do nowego obiektu CatInfoFormatter. Jeśli później każdy dziennik musi również zostać zapisany w pliku, możesz dodać CatToFileLoggerobiekt, który to robi.

Posiadacze danych a obiekty - podsumowanie

Dlaczego miałbyś to wszystko robić? Ponieważ teraz zmiana klasy zmienia tylko jedną rzecz ( sposób, w jaki kot jest zalogowany w przykładzie). Jeśli zmieniasz obiekt, zmieniasz tylko sposób wykonania określonej akcji; jeśli zmienisz właściciela danych, zmienisz tylko, jakie dane są przechowywane i / lub w jaki sposób są przechowywane.

Zmiana danych może również oznaczać sposób, w jaki coś zostało zrobione, musi się również zmienić, ale zmiana ta nie nastąpi, dopóki sam nie zmienisz odpowiedzialnego obiektu.

Przesada?

To rozwiązanie może wydawać się nieco przesadne. Pozwólcie, że powiem szczerze: tak jest . Jeśli cały twój program jest tak duży jak w przykładzie, nie rób żadnego z powyższych - po prostu wybierz jeden z proponowanych sposobów za pomocą rzutu monetą. Nie ma niebezpieczeństwa mylące inny kod, jeśli nie ma żaden inny kod.

Jednak każde „poważne” (= więcej niż skrypt lub 2) oprogramowanie jest prawdopodobnie na tyle duże, że skorzystałoby z takiej struktury.


Odpowiedź

Czy rozdzielenie większości klas na klasy DTO i klasy pomocnicze [...] jest dobre, czy też przeciwne?

Przekształcenie „większości klas” (bez dalszych kwalifikacji) w DTO / posiadaczy danych, nie jest anty-wzorcem, ponieważ w rzeczywistości nie jest to żaden wzorzec - jest mniej więcej losowy.

Oddzielanie danych i obiektów jest jednak postrzegane jako dobry sposób na utrzymanie kodu w czystości *. Czasami potrzebujesz tylko płaskiego, „anemicznego” DTO i to wszystko. Innym razem potrzebujesz metod egzekwowania sposobu przechowywania danych. Po prostu nie umieszczaj funkcjonalności programu z danymi.


* To jest „czyste” z wielką literą C, jak tytuł książki, ponieważ - pamiętaj pierwszy akapit - chodzi o jedną szkołę myślenia, podczas gdy są też inne.

R. Schmitz
źródło