Czy „unikanie problemu jo-jo” jest ważnym powodem pozwalającym na „prymitywną obsesję”?

42

Według Kiedy prymitywna obsesja nie jest zapachem kodu? , Powinienem utworzyć obiekt ZipCode reprezentujący kod pocztowy zamiast obiektu String.

Jednak z mojego doświadczenia wolę widzieć

public class Address{
    public String zipCode;
}

zamiast

public class Address{
    public ZipCode zipCode;
}

ponieważ myślę, że ten ostatni wymaga ode mnie przejścia do klasy ZipCode, aby zrozumieć program.

I uważam, że muszę przechodzić między wieloma klasami, aby zobaczyć definicję, jeśli wszystkie prymitywne pola danych zostaną zastąpione przez klasę, która wydaje się cierpieć z powodu problemu jo-jo (anty-wzorzec).

Chciałbym więc przenieść metody ZipCode do nowej klasy, na przykład:

Stary:

public class ZipCode{
    public boolean validate(String zipCode){
    }
}

Nowy:

public class ZipCodeHelper{
    public static boolean validate(String zipCode){
    }
}

tak więc tylko ten, kto musi zweryfikować kod pocztowy, będzie zależał od klasy ZipCodeHelper. I znalazłem kolejną „korzyść” z utrzymywania prymitywnej obsesji: sprawia, że ​​klasa wygląda jak jej zserializowana forma, jeśli taka istnieje, na przykład: tablica adresowa z kolumną łańcuchową zipCode.

Moje pytanie brzmi: czy „unikanie problemu jo-jo” (przejście między definicjami klas) jest ważnym powodem pozwalającym na „prymitywną obsesję”?

ocomfd
źródło
9
@ jpmc26 Byłbyś w szoku, widząc, jak skomplikowany jest nasz kod pocztowy - nie mówiąc, że jest
poprawny
9
@ jpmc26, nie widzę, jak przechodzisz od „złożonego” do „źle zaprojektowanego”. Skomplikowany kod jest często wynikiem prostego kontaktu kodu ze złożonością świata rzeczywistego, a nie idealnego świata, jaki chcielibyśmy istnieć. „Wracam do tej dwustronicowej funkcji. Tak, wiem, to tylko prosta funkcja wyświetlania okna, ale wyrosło na niej trochę włosów i inne rzeczy i nikt nie wie dlaczego. Cóż, powiem ci dlaczego: to są błędy poprawki ”.
Kyralessa
19
@ jpmc26 - celem zawijania obiektów takich jak ZipCode jest bezpieczeństwo typu. Kod pocztowy nie jest ciągiem, to kod pocztowy. Jeśli funkcja oczekuje kodu pocztowego, należy przekazać kod pocztowy, a nie ciąg znaków.
Davor Ždralo
4
Wydaje się to bardzo specyficzne dla danego języka, różne języki robią tutaj różne rzeczy. @ DavorŽdralo W tym samym odcinku powinniśmy również dodać wiele typów liczbowych. „tylko dodatnie liczby całkowite”, „tylko liczby parzyste” mogą być również typami.
paul23
6
@ paul23 Tak, rzeczywiście, a głównym powodem, dla którego ich nie mamy, jest to, że wiele języków nie obsługuje eleganckich sposobów ich definiowania. Całkowicie uzasadnione jest zdefiniowanie „wieku” jako innego rodzaju niż „temperatura w stopniach Celsjusza”, jeśli tylko po to, aby „userAge == currentTemperature” było wykrywane jako nonsens.
IMSoP

Odpowiedzi:

116

Zakłada się, że nie trzeba jo-jo do klasy ZipCode, aby zrozumieć klasę Adres. Jeśli ZipCode jest dobrze zaprojektowany, powinno być oczywiste, co robi po przeczytaniu klasy Address.

Programy nie są odczytywane od końca do końca - zazwyczaj programy są zbyt złożone, aby to umożliwić. Nie możesz jednocześnie przechowywać całego kodu w programie. Używamy więc abstrakcji i enkapsulacji, aby „podzielić” program na znaczące jednostki, abyś mógł spojrzeć na jedną część programu (powiedzmy klasę Adres) bez konieczności odczytywania całego kodu, od którego zależy.

Na przykład jestem pewien, że nie odczytujesz jo-jo odczytywania kodu źródłowego dla String za każdym razem, gdy napotkasz String w kodzie.

Zmiana nazwy klasy z ZipCode na ZipCodeHelper sugeruje, że istnieją teraz dwie osobne koncepcje: kod pocztowy i pomocnik kodu pocztowego. Tak dwa razy bardziej skomplikowane. A teraz system typów nie może pomóc w odróżnieniu dowolnego ciągu od prawidłowego kodu pocztowego, ponieważ mają ten sam typ. W tym przypadku właściwa jest „obsesja”: sugerujesz bardziej złożoną i mniej bezpieczną alternatywę tylko dlatego, że chcesz uniknąć prostego typu opakowania wokół prymitywów.

Używanie prymitywów jest uzasadnione przez IMHO w przypadkach, gdy nie ma sprawdzania poprawności lub innej logiki w zależności od tego konkretnego typu. Ale gdy tylko dodasz dowolną logikę, jest o wiele prostsze, jeśli logika ta jest enkapsulowana z typem.

Jeśli chodzi o serializację, myślę, że to brzmi jak ograniczenie w używanym frameworku. Z pewnością powinieneś być w stanie serializować kod pocztowy do ciągu lub odwzorować go na kolumnę w bazie danych.

JacquesB
źródło
2
Zgadzam się z częścią „znaczących jednostek” (główną), ale nie tak bardzo, że weryfikacja kodu pocztowego i kodu pocztowego to ta sama koncepcja. ZipCodeHelper(do którego wolałbym zadzwonić ZipCodeValidator) może równie dobrze ustanowić połączenie z usługą internetową, aby wykonać swoją pracę. To nie byłoby częścią pojedynczej odpowiedzialności „przechowuj dane kodu pocztowego”. Uczynienie systemu typów niedozwoleniem niepoprawnych kodów pocztowych może być nadal osiągnięte poprzez uczynienie ZipCodekonstruktora ekwiwalentem pakietu Java-prywatnym i wywołanie go za pomocą parametru, ZipCodeFactoryktóry zawsze wywołuje weryfikator.
R. Schmitz
16
@ R. Schmitz: Nie to oznacza „odpowiedzialność” w sensie zasady pojedynczej odpowiedzialności. Ale w każdym razie powinieneś oczywiście użyć tylu klas, ile potrzebujesz, pod warunkiem, że zamkniesz kod pocztowy i jego poprawność. OP sugeruje pomocnika zamiast enkapsulacji kodu pocztowego, co jest złym pomysłem.
JacquesB
1
Chcę z szacunkiem się nie zgodzić. SRP oznacza, że ​​klasa powinna mieć „jeden i jedyny powód do zmiany” (zmiana „z czego składa się kod pocztowy” w porównaniu z „w jaki sposób jest sprawdzany”). Ten konkretny przypadek jest dalej omawiany w książce Clean Code : „ Obiekty ukrywają swoje dane za abstrakcjami i ujawniają funkcje, które działają na tych danych. Struktura danych ujawnia swoje dane i nie ma żadnych znaczących funkcji. ” - ZipCodebyłaby „strukturą danych” i ZipCodeHelper„obiekt”. W każdym razie uważam, że zgadzamy się, że nie powinniśmy przekazywać połączeń internetowych do konstruktora ZipCode.
R. Schmitz
9
Używanie prymitywów jest uzasadnione przez IMHO w przypadkach, gdy nie ma sprawdzania poprawności lub innej logiki w zależności od tego konkretnego typu. => Nie zgadzam się. Nawet jeśli wszystkie wartości są prawidłowe, nadal wolałbym przekazywać semantykę do języka niż używać prymitywów. Jeśli funkcja może zostać wywołana na typie prymitywnym, który nie jest sensowny dla jego obecnego użycia semantycznego, to nie powinien być typem prymitywnym, powinien być typem właściwym z określonymi funkcjami sensownymi. (Jako przykład użycie intjako ID pozwala na pomnożenie ID przez ID ...)
Matthieu M.
@ R. Schmitz Myślę, że kody pocztowe są kiepskim przykładem tego, co czynisz. Coś, co często się zmienia, może być kandydatem do oddzielnych Fooi FooValidatorklas. My mogliśmy mieć ZipCodeklasę, która sprawdza format i ZipCodeValidatorże uderza jakąś usługę sieci Web, aby sprawdzić, czy prawidłowo sformatowany ZipCodejest rzeczywiście obecny. Wiemy, że zmieniają się kody pocztowe. Ale praktycznie będziemy mieć listę poprawnych kodów pocztowych zawartych w ZipCodelub w lokalnej bazie danych.
Nie
55

Jeśli można zrobić:

new ZipCode("totally invalid zip code");

Konstruktor ZipCode:

ZipCodeHelper.validate("totally invalid zip code");

Potem złamałeś enkapsulację i dodałeś dość niemądrą zależność do klasy ZipCode. Jeśli konstruktor nie wywołuje, oznacza ZipCodeHelper.validate(...)to, że masz wyodrębnioną logikę na własnej wyspie bez jej wymuszania. Możesz tworzyć nieprawidłowe kody pocztowe.

validateMetoda powinna być statyczna metoda na klasie kod pocztowy. Teraz znajomość „poprawnego” kodu pocztowego jest dołączona do klasy ZipCode. Biorąc pod uwagę, że twoje przykłady kodu wyglądają jak Java, konstruktor ZipCode powinien zgłosić wyjątek, jeśli podano nieprawidłowy format:

public class ZipCode {
    private String zipCode;

    public ZipCode(string zipCode) {
        if (!validate(zipCode))
            throw new IllegalFormatException("Invalid zip code");

        this.zipCode = zipCode;
    }

    public static bool validate(String zipCode) {
        // logic to check format
    }

    @Override
    public String toString() {
        return zipCode;
    }
}

Konstruktor sprawdza format i zgłasza wyjątek, zapobiegając w ten sposób tworzeniu nieprawidłowych kodów pocztowych, a validatemetoda statyczna jest dostępna dla innego kodu, więc logika sprawdzania formatu jest zawarta w klasie ZipCode.

W tym wariancie klasy ZipCode nie ma „jo-jo”. Nazywa się to po prostu właściwym programowaniem obiektowym.


Zignorujemy również internacjonalizację, co może wymagać innej klasy o nazwie ZipCodeFormat lub PostalService (np. PostalService.isValidPostalCode (...), PostalService.parsePostalCode (...) itp.).

Greg Burghardt
źródło
28
Uwaga: Główną zaletą podejścia @Greg Burkhardt jest to, że jeśli ktoś da ci obiekt ZipCode, możesz zaufać, że zawiera on prawidłowy ciąg znaków bez konieczności ponownego sprawdzania, ponieważ jego typ i fakt, że został pomyślnie zbudowany, daje ci ta gwarancja. Jeśli zamiast tego przekazałeś ciągi znaków, możesz poczuć potrzebę „potwierdzenia poprawności (zipCode)” w różnych miejscach kodu, aby upewnić się, że masz prawidłowy kod pocztowy, ale z pomyślnie zbudowanym obiektem ZipCode możesz zaufać jego zawartość jest ważna bez konieczności ponownego sprawdzania.
Some Guy
3
@ R. Schmitz: ZipCode.validateMetoda ta polega na sprawdzeniu wstępnym, które można wykonać przed wywołaniem konstruktora, który zgłosi wyjątek.
Greg Burghardt
10
@ R. Schmitz: Jeśli obawiasz się irytującego wyjątku, alternatywnym podejściem do budowy jest uczynienie konstruktora ZipCode prywatnym i zapewnienie publicznej statycznej funkcji fabrycznej (Zipcode.create?), Która wykonuje weryfikację przekazanych parametrów, zwraca null, jeśli się nie powiedzie, a w przeciwnym razie konstruuje obiekt ZipCode i zwraca go. Dzwoniący zawsze będzie musiał oczywiście sprawdzić zerową wartość zwracaną. Z drugiej strony, jeśli masz na przykład nawyk zawsze sprawdzania poprawności (regex? Validate? Itd.) Przed zbudowaniem kodu pocztowego, wyjątek może nie być tak irytujący w praktyce.
Some Guy
11
Możliwa jest również funkcja fabryczna, która zwraca Opcjonalny <ZipCode>. Wówczas osoba dzwoniąca nie ma innego wyjścia, jak jawnie poradzić sobie z możliwą awarią funkcji fabrycznej. Niezależnie od tego, w obu przypadkach błąd zostanie wykryty gdzieś w pobliżu miejsca, w którym został utworzony, a nie być może znacznie później, dzięki kodowi klienta z dala od pierwotnego problemu.
Some Guy
6
Nie możesz samodzielnie zweryfikować kodu pocztowego, więc nie rób tego. Naprawdę potrzebujesz obiektu Country, aby wyszukać reguły sprawdzania poprawności ZipCode / PostCode.
Joshua
11

Jeśli często zmagasz się z tym pytaniem, być może używany język nie jest odpowiednim narzędziem do pracy? Tego rodzaju „prymitywy typowane w domenie” są trywialnie łatwe do wyrażenia, na przykład w języku F #.

Tam możesz na przykład napisać:

type ZipCode = ZipCode of string
type Town = Town of string

type Adress = {
  zipCode: ZipCode
  town: Town
  //etc
}

let adress1 = {
  zipCode = ZipCode "90210"
  town = Town "Beverly Hills"
}

let faultyAdress = {
  zipCode = "12345"  // <-Compiler error
  town = adress1.zipCode // <- Compiler error
}

Jest to bardzo przydatne w celu uniknięcia typowych błędów, takich jak porównywanie identyfikatorów różnych podmiotów. A ponieważ te prymitywne typy są znacznie lżejsze niż klasy C # lub Java, w końcu ich użyjesz.

Guran
źródło
Ciekawe - jak by to wyglądało, gdybyś chciał wymusić weryfikację ZipCode?
Hulk
4
@Hulk Możesz pisać w stylu OO w F # i przekształcać typy w klasy. Wolę jednak styl funkcjonalny, deklarując typ za pomocą ZipCode = prywatny ZipCode ciągu i dodając moduł ZipCode z funkcją tworzenia. Oto kilka przykładów: gist.github.com/swlaschin/54cfff886669ccab895a
Guran
@ Bent-Tranberg Dzięki za edycję. Masz rację, prosty skrót typu nie zapewnia bezpieczeństwa typu kompilacji.
Guran
Jeśli wysłano mi mój pierwszy komentarz, który usunąłem, przyczyną było to, że najpierw źle zrozumiałem twoje źródło. Nie przeczytałem go wystarczająco uważnie. Kiedy próbowałem go skompilować, w końcu zdałem sobie sprawę, że tak naprawdę próbujesz to dokładnie zademonstrować, więc postanowiłem edytować, aby wszystko było w porządku.
Bent Tranberg
Tak. Moje oryginalne źródło było prawidłowe, niestety, włączając w to przykład, który miał być NIEPRAWIDŁOWY. Doh! Powinien po prostu połączyć się z Wlaschinem zamiast pisać sam kod :) fsharpforfunandprofit.com/posts/…
Guran
6

Odpowiedź zależy całkowicie od tego, co naprawdę chcesz zrobić z kodami pocztowymi. Oto dwie skrajne możliwości:

(1) Wszystkie adresy znajdują się w jednym kraju. W ogóle żadnych wyjątków. (Np. Żaden klient zagraniczny lub żaden pracownik, którego prywatny adres znajduje się za granicą, gdy pracują dla klienta zagranicznego.) Ten kraj ma kody pocztowe i można oczekiwać, że nigdy nie będą poważnie problematyczne (tj. Nie wymagają swobodnego wprowadzania danych np. „obecnie D4B 6N2, ale zmienia się co 2 tygodnie”). Kody pocztowe służą nie tylko do adresowania, ale także do sprawdzania poprawności informacji o płatności lub podobnych celów. - W tych okolicznościach klasa kodu pocztowego ma sens.

(2) Adresy mogą być w prawie każdym kraju, więc dziesiątki lub setki schematów adresowych z kodami pocztowymi lub bez nich (oraz z tysiącami dziwnych wyjątków i szczególnych przypadków) są istotne. Kod „ZIP” jest tak naprawdę proszony tylko o przypomnienie osobom z krajów, w których kody pocztowe są używane, aby nie zapomniały podać swoich. Adresy są używane tylko w taki sposób, że jeśli ktoś utraci dostęp do swojego konta i może udowodnić swoją nazwę i adres, dostęp zostanie przywrócony. - W tych okolicznościach klasy kodów pocztowych dla wszystkich odpowiednich krajów byłyby ogromnym wysiłkiem. Na szczęście w ogóle nie są potrzebne.


źródło
3

Inne odpowiedzi mówiły o modelowaniu domen OO i zastosowaniu bogatszego typu do reprezentowania twojej wartości.

Nie zgadzam się, szczególnie biorąc pod uwagę przykładowy kod, który opublikowałeś.

Ale zastanawiam się również, czy to faktycznie odpowiada tytułowi twojego pytania.

Rozważ następujący scenariusz (zaczerpnięty z faktycznego projektu, nad którym pracuję):

Masz zdalną aplikację na urządzeniu polowym, która komunikuje się z twoim serwerem centralnym. Jednym z pól DB wpisu urządzenia jest kod pocztowy adresu, pod którym znajduje się urządzenie polowe. Nie obchodzi Cię kod pocztowy (ani żaden inny adres adresu w tej sprawie). Wszyscy, którzy się tym przejmują, znajdują się po drugiej stronie granicy HTTP: akurat jesteś jedynym źródłem prawdy dla danych. Nie ma miejsca w modelowaniu domen. Po prostu go nagrywasz, zatwierdza, przechowuje i na żądanie przetasowuje w obiekcie blob JSON do innych miejsc.

W tym scenariuszu robienie czegokolwiek poza sprawdzaniem poprawności wstawki za pomocą ograniczenia wyrażenia regularnego SQL (lub jego odpowiednika ORM) jest prawdopodobnie przesadą w stosunku do odmiany YAGNI.

Jared Smith
źródło
6
Ograniczenie wyrażenia regularnego SQL może być postrzegane jako typ kwalifikowany - w bazie danych kod pocztowy nie jest przechowywany jako „VarChar”, ale „VarChar ograniczony przez tę regułę”. W niektórych systemach DBMS można łatwo nadać temu ograniczeniu typu + nazwę jako „typ domeny” wielokrotnego użytku, a my jesteśmy z powrotem w zalecanym miejscu, aby nadać danym znaczący typ. Zasadniczo zgadzam się z twoją odpowiedzią, ale nie sądzę, że ten przykład pasuje; lepszym przykładem byłoby, jeśli dane są „surowymi danymi czujnika”, a najbardziej znaczącym typem jest „tablica bajtów”, ponieważ nie masz pojęcia, co oznaczają dane.
IMSoP
@IMSoP interesujący punkt. Nie jestem pewien, czy się zgadzam: możesz sprawdzić poprawność łańcucha pocztowego w Javie (lub innym języku) za pomocą wyrażenia regularnego, ale nadal możesz traktować go jako ciąg zamiast bogatszego typu. W zależności od logiki domeny może być wymagana dalsza manipulacja (na przykład upewnienie się, że kod pocztowy pasuje do stanu, co byłoby trudne / niemożliwe do zweryfikowania za pomocą wyrażenia regularnego).
Jared Smith
Państwo mogło , ale tak szybko, jak to zrobić, jesteś przypisując mu pewne zachowania specyficzne dla domeny, i to jest dokładnie to, co mówią cytowane teksty powinny prowadzić do utworzenia niestandardowego typu. Pytanie nie brzmi, czy możesz to zrobić w jednym, czy drugim stylu, ale czy powinieneś , zakładając, że twój język programowania daje wybór.
IMSoP
Możesz modelować coś takiego jak a RegexValidatedString, zawierający sam łańcuch i wyrażenie regularne użyte do jego sprawdzenia. Ale chyba, że ​​każda instancja ma unikalny regex (co jest możliwe, ale mało prawdopodobne), wydaje się to trochę głupie i marnowanie pamięci (i być może czas kompilacji regex). Więc albo umieszczasz wyrażenie regularne w osobnej tabeli i zostawiasz klucz wyszukiwania w każdej instancji, aby go znaleźć (co jest prawdopodobnie gorsze ze względu na pośrednie), lub możesz znaleźć sposób, aby zapisać go raz dla każdego wspólnego typu dzielenia wartości z tą regułą - - np. pole statyczne na typie domeny lub równoważnej metodzie, jak powiedział IMSoP.
Miral
2

ZipCodeAbstrakcji mogłoby mieć sens tylko wtedy, gdy Addressklasa nie mieć również TownNamewłaściwość. W przeciwnym razie masz pół abstrakcji: kod pocztowy oznacza miasto, ale te dwa powiązane fragmenty informacji znajdują się w różnych klasach. To nie ma sensu.

Jednak nawet wtedy nie jest to prawidłowe zastosowanie (a raczej rozwiązanie) prymitywnej obsesji; który, jak rozumiem, koncentruje się głównie na dwóch rzeczach:

  1. Używanie prymitywów jako wejściowych (lub nawet wyjściowych) wartości metody, szczególnie gdy potrzebny jest zbiór prymitywów.
  2. Klasy, które z czasem zyskują dodatkowe właściwości, nigdy nie zastanawiając się, czy niektóre z nich powinny zostać zgrupowane w ich własną podklasę.

Twoja sprawa nie jest żadna. Adres to dobrze zdefiniowana koncepcja z wyraźnie niezbędnymi właściwościami (ulica, numer, kod pocztowy, miasto, stan, kraj, ...). Nie ma żadnego powodu do rozbicia tych danych, ponieważ ma on jedną odpowiedzialność: wyznaczenie lokalizacji na Ziemi. Adres wymaga wszystkich tych pól, aby był znaczący. Połowa adresu jest bezcelowa.

W ten sposób wiesz, że nie musisz dalej dzielić: dalsze rozbicie go zniszczyłoby funkcjonalną intencję Addressklasy. Podobnie, nie potrzebujesz Namepodklasy do użycia w Personklasie, chyba że Name(bez osoby związanej) jest sensownym pojęciem w Twojej domenie. Które to (zwykle) nie jest. Nazwiska służą do identyfikacji osób, zwykle same nie mają żadnej wartości.

Flater
źródło
1
@RikD: Z odpowiedzi: „nie potrzebujesz Namepodklasy do użycia w Personklasie, chyba że Nazwa (bez osoby związanej) jest znaczącym pojęciem w Twojej domenie ”. Gdy masz niestandardową weryfikację nazw, nazwa staje się znaczącym pojęciem w Twojej domenie; który wyraźnie wspomniałem jako prawidłowy przypadek użycia dla użycia podtypu. Po drugie, do sprawdzania poprawności kodu pocztowego wprowadzasz dodatkowe założenia, takie jak kody pocztowe, które muszą być zgodne z formatem danego kraju. Poruszasz temat znacznie szerszy niż cel pytania OP.
Flater
5
Adres to dobrze zdefiniowana koncepcja z wyraźnie niezbędnymi właściwościami (ulica, numer, kod pocztowy, miasto, stan, kraj). ” - Cóż, to po prostu źle. Aby uzyskać dobry sposób na poradzenie sobie z tym, zapoznaj się z formularzem adresu Amazon.
R. Schmitz
4
@ Flater Cóż, nie winię cię za to, że nie przeczytałeś pełnej listy kłamstw, ponieważ jest ona dość długa, ale dosłownie zawiera „Adresy będą miały ulicę”, „Adres wymaga zarówno miasta, jak i kraju”, „Adres będzie miał kod pocztowy ”itp., co jest sprzeczne z tym, co mówi cytowane zdanie.
R. Schmitz
8
@GregBurghardt „Kod pocztowy zakłada pocztę w Stanach Zjednoczonych, a nazwę miasta można uzyskać na podstawie kodu pocztowego. Miasta mogą mieć wiele kodów pocztowych, ale każdy kod pocztowy jest powiązany tylko z 1 miastem”. Zasadniczo nie jest to poprawne. Mam kod pocztowy, który jest używany głównie w sąsiednim mieście, ale nie ma tam mojej rezydencji. Kody pocztowe nie zawsze są zgodne z granicami rządowymi. Na przykład 42223 zawiera powiaty z TN i KY .
JimmyJames
2
W Austrii istnieje dolina dostępna tylko z Niemiec ( en.wikipedia.org/wiki/Kleinwalsertal ). Istnieje specjalny traktat dla tego regionu, który między innymi obejmuje również to, że adresy w tym obszarze mają zarówno austriacki, jak i niemiecki kod pocztowy. Ogólnie rzecz biorąc, nie można nawet założyć, że adres ma tylko jeden poprawny kod pocztowy;)
Hulk
1

Z artykułu:

Mówiąc bardziej ogólnie, problem jo-jo może również odnosić się do każdej sytuacji, w której dana osoba musi ciągle przeszukiwać różne źródła informacji, aby zrozumieć pojęcie.

Kod źródłowy jest odczytywany znacznie częściej niż jest zapisywany. Tak więc problem jo-jo, polegający na konieczności przełączania się między wieloma plikami jest problemem.

Jednak nie , problem jo-jo wydaje się o wiele bardziej istotny, gdy mamy do czynienia z głęboko zależnymi modułami lub klasami (które wzywają się do siebie nawzajem). To szczególny rodzaj koszmaru do przeczytania i prawdopodobnie to, co miał na myśli twórca problemu jo-jo.

Jednak - tak , ważne jest unikanie zbyt wielu warstw abstrakcji!

Wszystkie nietrywialne abstrakcje są do pewnego stopnia nieszczelne. - Prawo nieszczelnych abstrakcji.

Na przykład nie zgadzam się z założeniem poczynionym w odpowiedzi mmmaaa, że „ nie trzeba jo-jo, aby [(odwiedzić)] klasę ZipCode, aby zrozumieć klasę adresu”. Z mojego doświadczenia wynika, że ​​to robisz - przynajmniej za pierwszym razem, gdy czytasz kod. Jednak, jak inni zauważyli, że chwile, kiedy ZipCodeklasa jest właściwe.

YAGNI (Ya Ain't Gonna Need It) to lepszy wzorzec do naśladowania, aby uniknąć kodu Lasagna (kod ze zbyt wieloma warstwami) - abstrakcje, takie jak typy i klasy, są pomocne dla programisty i nie powinny być używane, chyba że pomoc.

Osobiście staram się „zapisywać wiersze kodu” (i oczywiście powiązane „zapisywanie plików / modułów / klas” itp.). Jestem pewien, że są tacy, którzy zastosowaliby do mnie epitet „prymitywnej obsesji” - uważam, że ważniejsze jest posiadanie kodu, który można łatwo zrozumieć, niż martwić się etykietami, wzorami i anty-wzorami. Właściwy wybór, kiedy należy utworzyć funkcję, moduł / plik / klasę lub umieścić funkcję we wspólnej lokalizacji, jest bardzo sytuacyjny. Z grubsza celuję w 3-100 funkcji liniowych, 80-500 plików liniowych i „1, 2, n” w przypadku kodu biblioteki wielokrotnego użytku ( SLOC - bez komentarzy i schematu; zazwyczaj chcę co najmniej 1 dodatkowy SLOC minimum na linię obowiązkową płyta grzewcza).

Większość pozytywnych wzorców powstało od deweloperów, którzy robili to dokładnie wtedy , gdy ich potrzebowali . O wiele ważniejsze jest nauczenie się pisania czytelnego kodu niż próba zastosowania wzorców bez rozwiązania tego samego problemu. Każdy dobry programista może wdrożyć wzór fabryczny, nie widząc go wcześniej w rzadkim przypadku, gdy jest on odpowiedni dla ich problemu. Użyłem wzorca fabrycznego, wzorca obserwatora i prawdopodobnie setek innych, nie znając ich nazwy (tzn. Czy istnieje „wzorzec przypisania zmiennej”?). Dla zabawnego eksperymentu - zobacz, ile wzorców GoF jest wbudowanych w język JS - przestałem liczyć po około 12-15 w 2009 roku. Wzorzec Factory jest tak prosty, jak na przykład zwracanie obiektu z konstruktora JS - nie trzeba WidgetFactory.

Więc - tak , czasami ZipCode jest to dobra klasa. Jednak nie , problem jo-jo nie jest ściśle istotny.

Iiridayn
źródło
0

Problem jo-jo jest istotny tylko wtedy, gdy trzeba przewracać w przód iw tył. Jest to spowodowane jedną lub dwiema rzeczami (czasami obie):

  1. Złe nazewnictwo. ZipCode wydaje się w porządku, ZoneImprovementPlanCode będzie wymagał spojrzenia większości ludzi (i tych, którzy nie będą pod wrażeniem).
  2. Niewłaściwe połączenie. Powiedzmy, że klasa ZipCode ma wyszukiwanie numeru kierunkowego. Możesz pomyśleć, że ma to sens, ponieważ jest poręczny, ale tak naprawdę nie jest powiązany z kodem pocztowym, a włożenie go do niego oznacza, że ​​teraz ludzie nie wiedzą, gdzie się udać.

Jeśli możesz spojrzeć na nazwę i mieć rozsądne pojęcie o tym, co ona robi, a metody i właściwości robią racjonalnie oczywiste rzeczy, nie musisz patrzeć na kod, możesz go po prostu użyć. Taki jest przede wszystkim cel klas - są to modułowe fragmenty kodu, których można używać i rozwijać w izolacji. Jeśli musisz spojrzeć na coś innego niż interfejs API, aby zobaczyć, co robi klasa, jest to w najlepszym wypadku częściowa awaria.

jmoreno
źródło
-1

Pamiętaj, że nie ma srebrnej kuli. Jeśli piszesz niezwykle prostą aplikację, którą należy szybko przeszukać, prosty ciąg znaków może to zrobić. Jednak w 98% przypadków obiekt Value opisany przez Erica Evansa w DDD byłby idealnie dopasowany. Możesz łatwo zobaczyć wszystkie korzyści, jakie zapewniają obiekty wartości, czytając.

Alexios Plomaritis
źródło