Czy rozwiązanie powinno być tak ogólne, jak to możliwe, czy tak szczegółowe, jak to możliwe?


124

Powiedzmy, że mam encję, która ma atrybut „typ”. Może być ponad 20 możliwych typów.

Teraz jestem proszony o zaimplementowanie czegoś, co pozwoliłoby zmienić typ z A-> B, co jest jedynym przypadkiem użycia.

Czy powinienem więc wdrożyć coś, co pozwala na dowolne zmiany typu, o ile są to prawidłowe typy? A może powinienem pozwolić TYLKO na zmianę z A-> B zgodnie z wymaganiami i odrzucić wszelkie inne zmiany typu, takie jak B-> A lub A-> C?

Widzę zalety i wady z obu stron, gdzie ogólne rozwiązanie oznaczałoby mniej pracy w przypadku pojawienia się podobnych wymagań w przyszłości, ale oznaczałoby także większą szansę na błąd (chociaż w tym przypadku w 100% kontrolujemy osobę dzwoniącą punkt).
Konkretne rozwiązanie jest mniej podatne na błędy, ale wymaga więcej pracy w przyszłości, jeśli pojawią się podobne wymagania.

Ciągle słyszę, że dobry programista powinien starać się przewidzieć zmianę i zaprojektować system, aby można go było łatwo rozbudować w przyszłości, co wydaje się być rozwiązaniem ogólnym?

Edytować:

Dodanie więcej szczegółów do mojego nie tak specyficznego przykładu: Rozwiązanie „ogólne” w tym przypadku wymaga mniej pracy niż rozwiązanie „szczególne”, ponieważ konkretne rozwiązanie wymaga sprawdzania poprawności na starym i nowym typie, podczas gdy rozwiązanie ogólne wystarczy zweryfikować nowy typ.


97
To interesujące pytanie. Rozmawiałem o czymś takim z moimi grampami (bardzo, bardzo starym programistą timerów), a jego odpowiedź brzmiała: „najbardziej ogólna rzecz, która rozwiązuje konkretny problem”, co sprowadza się do fantazji ” To zależy". Ogólne deklaracje w Software Development rzadko działają - wszystko należy zawsze rozpatrywać indywidualnie.
T. Sar

2
@ T.Sar dodaj to jako odpowiedź, a ja głosuję :-)
Christophe

4
Co jest Twoim zdaniem „ogólnym rozwiązaniem”? Wyjaśnij też, co rozumiesz przez „jedyny przypadek użycia”. Czy masz na myśli, że dozwolone jest tylko przejście z A-> B, czy tylko to przejście jest określone, i przejście od jakiegokolwiek innego stanu do innego nie byłoby warunkiem błędu. Jako programista musisz poprosić autora przypadku użycia o wyjaśnienie. Jeśli żadne inne przejście nie jest dozwolone, a Twój kod na to pozwala, niezależnie od tego, kto kontroluje osobę dzwoniącą, Twój kod nie spełnia wymagań.
RibaldEddie

5
Zasada, że ​​jeśli X jest dobrym pomysłem, to tylko X przez cały czas musi być optymalna, wydaje się mieć szczególny apel do programistów (lub przynajmniej grupy wokalnej), ale weź to pod uwagę: podstawowe zasady programowania są jak przysłowia, ponieważ często można znaleźć parę o przeciwnych implikacjach. Jest to znak, że powinieneś użyć swojego osądu (jak zalecają tu odpowiedzi) i strzeż się dogmatów.
sdenham

2
Przedwczesne uogólnienie może powodować tyle samo zgagi, co przedwczesna optymalizacja - kończy się pisanie dużej ilości kodu, który może nigdy nie zostać wykorzystany. Najpierw rozwiązane dla konkretnego problemu, a następnie uogólnione w miarę potrzeby.
John Bode

Odpowiedzi:


296

Moja ogólna zasada:

  1. przy pierwszym napotkaniu problemu rozwiąż tylko określony problem (jest to zasada YAGNI )
  2. za drugim razem, gdy napotkasz ten sam problem, rozważ uogólnienie pierwszego przypadku, jeśli nie jest to dużo pracy
  3. gdy będziesz mieć trzy konkretne przypadki, w których będziesz mógł użyć wersji uogólnionej, powinieneś zacząć naprawdę planować wersję uogólnioną - do tej pory powinieneś zrozumieć problem wystarczająco dobrze, aby móc go uogólnić.

Oczywiście jest to wytyczna, a nie twarda i szybka reguła: prawdziwą odpowiedzią jest skorzystanie z najlepszej oceny każdego przypadku z osobna.


1
Czy możesz wyjaśnić, co to jest YAGNI?
Bernhard

7
@Bernhard Nie będziesz go potrzebował . Bardzo przydatna zasada projektowania oprogramowania.
Angew

4
„W thing1, thing2, pomyśl o użyciu tablicy. W thing1, thing2, thing3prawie na pewno użyj thing[]tablicy.” jest podobną ogólną zasadą przy podejmowaniu decyzji między wieloma zmiennymi lub pojedynczą tablicą.
Joker_vD,

15
Kolejny sposób na sformułowanie reguły nr 1: „Nie można uogólniać jednej rzeczy”.
Wayne Conrad,

2
@ SantiBailors: Jak mówi doktor Brown w swojej odpowiedzi, często znacznie przeceniamy ilość zmarnowanego wysiłku, którą moglibyśmy poświęcić, budując pierwszy, który należy wyrzucić. W „ The Mythical Man-Month” Fred Brooks mówi: „Planuj wyrzucić jednego - i tak to zrobisz”. To powiedziawszy: jeśli od razu natrafisz na więcej niż jedno zastosowanie do czegoś - na przykład poprzez dostarczenie zestawu wymagań, w których wyraźnie będziesz musiał rozwiązać ten sam problem więcej niż jeden raz - masz już więcej niż jedną sprawę do uogólnienia z, i to jest całkowicie w porządku i nie jest sprzeczne z moją odpowiedzią.
Daniel Pryden,

95

Konkretne rozwiązanie [...] wymaga więcej pracy w przyszłości, jeśli potrzebne będą podobne wymagania

Słyszałem ten argument kilkadziesiąt razy i - z mojego doświadczenia - regularnie okazuje się on błędem. Jeśli uogólnisz teraz lub później, kiedy pojawi się drugi podobny wymóg, praca będzie prawie taka sama. Zatem nie ma absolutnie żadnego sensu inwestowanie w uogólnienie, jeśli nie wiesz, że wysiłek kiedykolwiek się opłaci.

(Oczywiście nie ma to zastosowania, gdy bardziej ogólne rozwiązanie jest mniej skomplikowane i wymaga mniej wysiłku niż konkretne, ale z mojego doświadczenia wynika, że ​​są to rzadkie przypadki. Taki scenariusz został później zredagowany w pytaniu i nie jest to jeden dotyczy mojej odpowiedzi).

Kiedy pojawia się „drugi podobny przypadek”, czas zacząć myśleć o uogólnieniu. Znacznie łatwiej będzie wtedy uogólnić poprawnie , ponieważ ten drugi wymóg daje ci scenariusz, w którym możesz sprawdzić, czy właściwe rzeczy są ogólne. Próbując uogólnić tylko dla jednego przypadku, strzelasz w ciemności. Szanse są duże, nadmiernie uogólniasz pewne rzeczy, których nie trzeba uogólniać, i tęsknisz za innymi częściami, które powinny. A kiedy pojawia się drugi przypadek i zdajesz sobie sprawę, że uogólniłeś niewłaściwe rzeczy, masz dużo więcej pracy do zrobienia, aby to naprawić.

Polecam więc odłożyć pokusę na robienie rzeczy „na wszelki wypadek”. Takie podejście doprowadzi do zwiększenia nakładów pracy i konserwacji tylko wtedy, gdy przegapisz okazję do uogólnienia trzy, cztery lub więcej razy, a następnie będziesz miał stos podobnego (tak zduplikowanego) kodu do utrzymania.


2
Dla mnie ta odpowiedź nie ma sensu w kontekście OP. Mówi, że poświęci mniej czasu na generalizowanie teraz, a mniej w przyszłości, chyba że w przyszłości będzie potrzeba konkretnej implementacji. Argumentujesz przeciwko „inwestowaniu dodatkowego wysiłku w uogólnienie”, podczas gdy OP zainwestuje dodatkowy wysiłek tylko wtedy, gdy się nie uogólni. (pomyśl) .... dziwne: $
msb

2
@msb: ten kontekst został dodany po napisaniu mojej odpowiedzi i jest sprzeczny z tym, co mówiła PO, szczególnie w części, którą cytowałem. Zobacz moją edycję.
Doc Brown

2
Ponadto uogólnione rozwiązanie będzie bardziej skomplikowane, więc zanim będziesz musiał go dostosować do drugiego przypadku, jego zrozumienie zajmie znacznie więcej czasu.
AndreKR,

4
Doc, na marginesie, ale nigdy nie zgadłbym, że nie jesteś native speakerem. Twoje odpowiedzi są zawsze dobrze napisane i dobrze uzasadnione.
user949300,

2
Wyobrażam sobie twój akcent, kiedy czytam twoje przyszłe odpowiedzi. :-)
user949300

64

TL; DR: zależy to od tego, co próbujesz rozwiązać.

Rozmawiałem na ten temat z moimi Grampami, kiedy rozmawialiśmy o tym, jak Func i Action w C # są niesamowite. My Gramps to bardzo stary programator czasowy, który był związany z kodami źródłowymi, odkąd oprogramowanie działało na komputerach, które zajmowały cały pokój.

Kilka razy zmieniał technologię w swoim życiu. Napisał kod w C, COBOL, Pascal, BASIC, Fortran, Smalltalk, Java i ostatecznie zaczął C # jako hobby. Nauczyłem się, jak programować z nim, siedząc na kolanach, gdy byłem diabłem, wycinając pierwsze wiersze kodu w niebieskim edytorze SideKick IBM. Kiedy miałem 20 lat, spędziłem już więcej czasu na kodowaniu niż granie na zewnątrz.

To trochę moje wspomnienia, więc przepraszam, jeśli nie jestem do końca praktyczny, gdy je powtarzam. Trochę mi się podobają te chwile.

Tak mi powiedział:


„Czy powinniśmy dążyć do uogólnienia problemu, czy rozwiązać go w określonym zakresie, pytasz? Cóż, to… pytanie”.

Gramps zatrzymał się na chwilę, aby pomyśleć o tym, jednocześnie ustalając położenie okularów na twarzy. Grał w grę match-3 na swoim komputerze, słuchając płyty Deep Purple na swoim starym systemie dźwiękowym.

„Cóż, to zależy od tego, jaki problem próbujesz rozwiązać”, powiedział mi. „Kuszące jest przekonanie, że istnieje jedno święte rozwiązanie dla wszystkich wyborów projektowych, ale nie ma jednego. Architektura oprogramowania jest jak ser, rozumiesz.”

„... Ser, Gramps?”

„Nie ważne, co myślisz o swoim ulubionym, zawsze znajdzie się ktoś, kto uważa, że ​​śmierdzi”.

Przez chwilę mrugałam zmieszana, ale zanim zdążyłam cokolwiek powiedzieć, Gramps kontynuował.

„Jak budujesz samochód, jak wybierasz materiał na część?”

„Myślę… Myślę, że to zależy od kosztów i tego, co ta część powinna zrobić, jak sądzę.”

„To zależy od problemu, który stara się rozwiązać część. Nie zrobisz opony wykonanej ze stali lub przedniej szyby wykonanej ze skóry. Wybierasz materiał, który najlepiej rozwiązuje problem, który masz pod ręką. Teraz czym jest ogólne rozwiązanie? Albo konkretne? Na jaki problem, na jaki przypadek użycia? Czy powinieneś zastosować pełne funkcjonalne podejście, aby zapewnić maksymalną elastyczność kodowi, który będzie użyty tylko raz? Czy powinieneś napisać bardzo specjalistyczny, delikatny kod do część twojego systemu, która będzie miała wiele zastosowań i być może wiele zmian? Takie wybory projektowe są podobne do materiałów, które wybierasz do części w samochodzie lub kształtu klocków Lego, które wybierasz do budowy małego domu Jaka klocki Lego jest najlepsza? ”

Starszy programista sięgnął po mały model pociągu Lego, który ma na swoim stole, zanim kontynuował.

„Możesz odpowiedzieć tylko na to, jeśli wiesz, po co ci ta cegła. Skąd, do diabła, dowiesz się, czy konkretne rozwiązanie jest lepsze niż ogólne, i odwrotnie, jeśli nawet nie wiesz, na czym polega problem? próbujesz rozwiązać? Nie widzisz przeszłości, której nie rozumiesz ”.

„..Czy właśnie zacytowałeś Matrycę?

"Co?"

„Nic, mów dalej”.

„Załóżmy, że próbujesz zbudować coś w systemie faktur krajowych. Wiesz, jak wygląda ten piekielny interfejs API i jego plik XML zawierający trzydzieści tysięcy wierszy od wewnątrz. Jak wyglądałoby„ ogólne ”rozwiązanie do tworzenia tego pliku na przykład? Plik jest pełen opcjonalnych parametrów, pełen spraw, z których powinny korzystać tylko bardzo specyficzne gałęzie biznesu. W większości przypadków możesz je bezpiecznie zignorować. Nie musisz tworzyć ogólnego systemu faktur, jeśli jedyną rzeczą, którą „ Zawsze będę sprzedawać buty. Po prostu stwórz system sprzedaży obuwia i spraw, aby był to najlepszy system fakturowania butów, który istnieje na rynku. Teraz, gdybyś musiał stworzyć system faktur dla dowolnego rodzaju klienta, w szerszym zakresie - zostać odsprzedane jako niezależny, ogólny system sprzedaży,na przykład - teraz interesujące jest wdrożenie tych opcji, które są używane tylko w przypadku gazu, żywności lub alkoholu.Teraz są to możliwe przypadki użycia. Wcześniej były to tylko hipotetyczne przypadki Nie używaj przypadków, a nie chcesz wdrożyć Nie używaj przypadków. „Nie używaj” to młodszy brat „ Nie potrzebujesz ”.

Gramps umieścił pociąg lego z powrotem na swoim miejscu i wrócił do swojej gry polegającej na dopasowywaniu trzech elementów.

„Aby więc wybrać ogólne lub konkretne rozwiązanie danego problemu, musisz najpierw zrozumieć, na czym polega ten problem. W przeciwnym razie zgadujesz, a zgadywanie jest zadaniem menedżerów, a nie programistów. Jak prawie wszystko w IT, to zależy ”.


Więc masz to. "To zależy". To chyba najpotężniejsze wyrażenie dwuliterowe, gdy myśli się o projektowaniu oprogramowania.


14
Jestem pewien, że w tej historii jest coś przydatnego, ale przeczytanie jej było zbyt bolesne, przepraszam
GoatInTheMachine,

28
Twój pierwszy post był krótką zabawną anegdotą - i oczywiście jest to kwestia opinii - ale jeśli naprawdę nie lubię czyjegoś stylu pisania, znajdę długie historie takie jak ten naprawdę
pobłażający

16
@ T.Sar nie słuchaj hejterów, twój post to klejnot przydatnych porad od guru. +1
LLlAMnYP

7
Część „architektura oprogramowania jest jak ser” była po prostu niesamowita. Rzeczywiście, ta odpowiedź to klejnot!
Mathieu Guindon,

3
Może ta odpowiedź też jest jak ser? ;) Ale proszę, „SmallTalk” powinno być „Smalltalk”, i tak, mam być , że facet, przepraszam.
fede s.

14

Przede wszystkim powinieneś spróbować przewidzieć, czy prawdopodobne jest, że taka zmiana nastąpi - a nie tylko odległe możliwości gdzieś poniżej linii.

Jeśli nie, zwykle lepiej jest teraz przejść do prostego rozwiązania i rozszerzyć je później. Całkiem możliwe, że będziesz miał wyraźniejszy obraz tego, co jest wtedy potrzebne.


13

Jeśli pracujesz w domenie, która jest dla ciebie nowa, niż zasada trzech , o której wspomniał Daniel Pryden, zdecydowanie powinna być zastosowana. W końcu, jak masz budować przydatne abstrakty, jeśli jesteś nowicjuszem w tej dziedzinie? Ludzie często są pewni siebie, jeśli chodzi o zdolność do wyłapywania abstrakcji, choć rzadko tak jest. Z mojego doświadczenia wynika, że ​​przedwczesna abstrakcja jest nie mniejszym złem niż powielanie kodu. Błędne abstrakcje są bardzo bolesne do zrozumienia. Czasami jeszcze bardziej bolesne dla refaktoryzacji.

Istnieje książka, w której poruszam moją uwagę na temat nieznanego obszaru, na którym pracuje programista. Składa się z określonych domen z wyodrębnionymi przydatnymi abstrakcjami.


Pytanie jest jasne na temat konkretnego problemu.
RibaldEddie

4
Odpowiedź jest wystarczająco ogólna, aby rozwiązać ten konkretny problem. Pytanie nie jest wystarczająco szczegółowe, aby otrzymać konkretny rachunek: jak widać, w pytaniu nie wymieniono żadnych szczegółów domeny. Nawet nazwy klas nie są określone (A i B się nie liczą).
Zapadlo

7
„Czy odpowiedź na przepełnienie stosu powinna być jak najbardziej ogólna, czy jak najbardziej szczegółowa?”
Lyndon White,

@LyndonWhite czy pytanie o przepełnienie stosu powinno być tak ogólne, jak to możliwe (zbyt szerokie!) Lub tak szczegółowe, jak to możliwe (jakkolwiek to się nazywa niepowodzenie!)? Ha.

4

Biorąc pod uwagę naturę twojego pytania, zakładając, że dobrze to zrozumiałem, faktycznie postrzegam to jako pytanie projektowe o cechach systemu centralnego, a nie pytanie o ogólne lub konkretne rozwiązania.

A jeśli chodzi o funkcje i możliwości centralnego systemu, najbardziej niezawodne są te, których nie ma. Opłaca się popierać minimalizm, zwłaszcza biorąc pod uwagę, że generalnie łatwiej jest centralnie dodać funkcjonalność, od dawna pożądaną, niż usunąć problematyczną funkcjonalność, długo niepożądaną, z licznymi zależnościami, ponieważ znacznie utrudniła pracę z systemem niż musi być przy zadawaniu niekończących się pytań projektowych przy każdej nowej funkcji.

W rzeczywistości, nie przewidując, czy będzie to często potrzebne w przyszłości, starałbym się unikać patrzenia na to jako zamiennik typu od A do B, jeśli to możliwe, a zamiast tego po prostu szukałem go jako sposobu na przekształcenie stanu A. Na przykład, ustaw niektóre pola w A, aby przekształciły się i wyglądały jak B dla użytkownika bez faktycznego zmieniania się w inny „typ” rzeczy - możesz potencjalnie stworzyć A sklep B prywatnie za pomocą funkcji kompozycji i wywoływania w B, gdy stan A jest ustawiony, aby wskazać, że powinien naśladować B, aby w miarę możliwości uprościć implementację. To powinno być bardzo proste i minimalnie inwazyjne rozwiązanie.

W każdym razie, powołując się na wiele innych, sugeruję pomyłkę po stronie unikania ogólnych rozwiązań w tym przypadku, ale tym bardziej, że zakładam, że rozważa się dodanie bardzo odważnych możliwości do systemu centralnego, i tam zasugeruj, że pomylę się z pominięciem tego, szczególnie na razie.


„tęsknisz za wszystkimi błędami, których nie kodujesz”. (z plakatu koszykówki: tęsknisz za wszystkimi

3

Trudno jest udzielić ogólnej odpowiedzi na ten konkretny problem ;-)

Im bardziej ogólny, tym więcej czasu zyskasz na przyszłe zmiany. Na przykład z tego powodu wiele programów do gier używa wzorca komponentu bytu zamiast budowania bardzo rozbudowanego, ale sztywnego systemu typów postaci i przedmiotów w grze.

Z drugiej strony, stworzenie czegoś ogólnego wymaga dużo czasu i wysiłku włożonego w projektowanie, które jest znacznie wyższe niż w przypadku czegoś bardzo konkretnego. Niesie to za sobą ryzyko nadmiernej inżynierii, a nawet zagubienia się w potencjalnych przyszłych wymaganiach.

Zawsze warto sprawdzić, czy istnieje naturalne uogólnienie, które zapewni ci przełom. Ostatecznie jednak chodzi o równowagę między wysiłkiem, który możesz teraz poświęcić, a wysiłkiem, którego możesz potrzebować w przyszłości.


3
  1. Hybrydowy. To nie musi być ani pytanie, ani pytanie. Możesz zaprojektować interfejs API dla konwersji typu ogólnego, jednocześnie wdrażając tylko konkretną konwersję, której potrzebujesz teraz. (Upewnij się tylko, że jeśli ktoś zadzwoni do Twojego ogólnego interfejsu API z nieobsługiwaną konwersją, zakończy się niepowodzeniem ze statusem błędu „nieobsługiwany”).

  2. Testowanie. W przypadku konwersji A-> B będę musiał napisać jeden (lub niewielką liczbę) testów. W przypadku ogólnej konwersji x-> y może być konieczne napisanie całej macierzy testów. To znacznie więcej pracy, nawet jeśli wszystkie konwersje korzystają z jednej ogólnej implementacji.

    Jeśli z drugiej strony okaże się, że istnieje ogólny sposób na przetestowanie wszystkich możliwych konwersji, oznacza to, że nie ma dużo więcej pracy i być może skłaniam się do wcześniejszego rozwiązania ogólnego.

  3. Sprzęganie. Konwerter z A na B może koniecznie znać szczegóły implementacji dotyczące A i B (ścisłe połączenie). Jeśli A i B wciąż ewoluują, oznacza to, że będę musiał ponownie przeglądać konwerter (i jego testy), co jest do bani, ale przynajmniej jest ograniczone do A i B.

    Jeśli wybrałbym ogólne rozwiązanie, które wymaga dostępu do szczegółów wszystkich typów, to nawet w miarę ewolucji C i D, być może będę musiał ciągle ulepszać ogólny konwerter (i kilka testów), co może mnie spowolnić nawet chociaż nikt jeszcze trzeba przekonwertować do C lub D .

    Jeśli zarówno konwersje ogólne, jak i specyficzne mogą być zaimplementowane w sposób, który jest luźno powiązany ze szczegółami typów, nie martwiłbym się tym. Jeśli jedno z nich można wykonać w luźny sposób, ale drugi wymaga ścisłego połączenia, to jest to mocny argument za podejściem luźno sprzężonym tuż za bramą.


2
Testowanie jest dobrym punktem. Dużą zaletą jest projektowanie ogólnych rozwiązań opartych na pojęciach z teorii kategorii , ponieważ często uzyskuje się bezpłatne twierdzenia, które dowodzą, że jedyna możliwa implementacja podpisu (akceptowana przez moduł sprawdzania typu kompilatora) jest poprawna , a przynajmniej że jeśli algorytm działa dla dowolnego określonego typu, musi również działać dla wszystkich innych typów.
leftaroundabout

Sądzę, że istnieje powód specyficzny dla domeny, dla którego poproszono o konwersję tylko jednego typu, co oznacza, że ​​większość konwersji typów byłaby nieprawidłowymi działaniami w domenie problemowej iz tego powodu aplikacja nie powinna obsługiwać jej, dopóki są oficjalnie dozwolone. Odpowiedzi te są najbliższe tej argumentacji.
Ralf Kleberhoff,

3

Czy rozwiązanie powinno być tak ogólne, jak to możliwe, czy tak szczegółowe, jak to możliwe?

To nie jest pytanie, na które można odpowiedzieć.

Najlepsze, co można racjonalnie uzyskać, to kilka heurystyk, które decydują o tym, jak ogólne lub szczegółowe jest wykonanie danego rozwiązania. Przepracowanie czegoś podobnego do poniższego procesu, zwykle przybliżenie pierwszego rzędu jest poprawne (lub wystarczająco dobre). Jeśli tak nie jest, przyczyna może być zbyt specyficzna dla domeny, aby można ją było szczegółowo omówić tutaj.

  1. Przybliżenie pierwszego rzędu: zwykła reguła trzech YAGNI opisana przez Daniela Pryden, Doc Brown, i in .

    Jest to ogólnie przydatna heurystyka, ponieważ jest to prawdopodobnie najlepsza możliwa metoda, która nie zależy od domeny i innych zmiennych.

    Tak więc początkowe założenie brzmi: robimy najbardziej konkretną rzecz.

  2. Przybliżenie drugiego rzędu: mówisz na podstawie swojej wiedzy eksperckiej w dziedzinie rozwiązań

    Rozwiązanie „ogólne” w tym przypadku wymaga mniej pracy niż rozwiązanie „specyficzne”

    więc może ponownie zinterpretować YAGNI jak polecając unikamy niepotrzebnego pracę , zamiast unikania niepotrzebnego ogólności. Możemy więc zmodyfikować nasze początkowe domniemanie i zamiast tego zrobić najłatwiejszą rzecz.

    Jeśli jednak Twoja wiedza dotycząca domeny rozwiązania wskazuje, że najłatwiejsze rozwiązanie może spowodować wiele błędów lub być trudne do odpowiedniego przetestowania lub powodować inne problemy, łatwiejsze kodowanie niekoniecznie jest wystarczającym powodem do zmiany naszego oryginalny wybór.

  3. Przybliżenie trzeciego rzędu: czy twoja wiedza w dziedzinie problemów sugeruje, że najłatwiejsze rozwiązanie jest w rzeczywistości poprawne, czy też dopuszczasz wiele przejść, o których wiesz, że są pozbawione znaczenia lub błędne?

    Jeśli proste, ale ogólne rozwiązanie wydaje się problematyczne lub nie masz pewności, czy potrafisz ocenić te zagrożenia, wykonanie dodatkowej pracy i pozostanie przy początkowych przypuszczeniach jest prawdopodobnie lepsze.

  4. Przybliżenie czwartego rzędu: czy twoja wiedza na temat zachowań klientów lub tego, w jaki sposób ta funkcja odnosi się do innych, priorytetów zarządzania projektem, lub ... jakieś inne nie ściśle techniczne względy modyfikują twoją obecną decyzję roboczą?


2

Odpowiedź na to pytanie nie jest łatwa. Wiele odpowiedzi dało heurystykę zbudowaną wokół reguły 3 lub czegoś podobnego. Wykraczanie poza takie zasady jest trudne.

Naprawdę naprawdę odpowiedzieć na to pytanie, trzeba wziąć pod uwagę, że twoja praca jest najbardziej prawdopodobne, aby nie realizować coś, co zmienia A-> B. Jeśli jesteś wykonawcą, być może jest to wymóg, ale jeśli jesteś pracownikiem, zatrudniono Cię do wykonywania wielu mniejszych zadań dla firmy. Zmiana A-> B to tylko jedno z tych zadań. Twoja firma będzie dbać o to, jak dobrze można wprowadzić przyszłe zmiany, nawet jeśli nie zostanie to określone w żądaniu. Aby znaleźć „TheBestImplementation (tm)”, musisz spojrzeć na większy obraz tego, o co tak naprawdę jesteś proszony, a następnie użyć go do interpretacji małej prośby o zmianę A-> B.

Jeśli jesteś niskopoziomowym programistą rozpoczynającym naukę po studiach, często wskazane jest zrobienie dokładnie tego, co ci kazano. Jeśli zostałeś zatrudniony jako architekt oprogramowania z 15-letnim doświadczeniem, zazwyczaj warto pomyśleć o dużych obrazach. Każda rzeczywista praca spadnie gdzieś pomiędzy „rób dokładnie to, co jest wąskie zadanie” i „pomyśl o dużym obrazie”. Przekonasz się, gdzie mieści się Twoja praca w tym spektrum, jeśli będziesz wystarczająco rozmawiać z ludźmi i wykonywać dla nich wystarczającą ilość pracy.

Mogę podać kilka konkretnych przykładów, w których twoje pytanie ma ostateczną odpowiedź na podstawie kontekstu. Rozważ przypadek, w którym piszesz oprogramowanie krytyczne dla bezpieczeństwa. Oznacza to, że zespół testowy wstał, aby upewnić się, że produkt działa zgodnie z obietnicą. Niektóre z tych zespołów testowych są zobowiązane do przetestowania każdej możliwej ścieżki w kodzie. Jeśli z nimi porozmawiasz, może się okazać, że jeśli uogólnisz to zachowanie, dodasz 30 000 USD do ich kosztów testowania, ponieważ będą musieli przetestować wszystkie te dodatkowe ścieżki. W takim przypadku nie dodawaj ogólnej funkcjonalności, nawet jeśli z tego powodu musisz powielić pracę 7 lub 8 razy. Zaoszczędź pieniądze firmy i rób dokładnie to, co podano w żądaniu.

Z drugiej strony rozważ, że tworzysz interfejs API, aby umożliwić klientom dostęp do danych w programie bazy danych utworzonym przez Twoją firmę. Klient prosi o zezwolenie na zmiany A-> B. Interfejsy API mają zazwyczaj aspekt złotych kajdanek: po dodaniu funkcji do interfejsu API zwykle nie należy usuwać tej funkcji (do następnego głównego numeru wersji). Wielu twoich klientów może nie być skłonnych zapłacić za aktualizację do następnego głównego numeru wersji, więc możesz utknąć w jakimkolwiek wybranym przez siebie rozwiązaniu przez długi czas. W takim przypadku zdecydowanie polecam utworzenie ogólnego rozwiązania od samego początku. Naprawdę nie chcesz tworzyć złego API pełnego jednorazowych zachowań.


1

Hmm ... mało kontekstu do odpowiedzi ... echo wcześniejszych odpowiedzi: „To zależy”.

W pewnym sensie musisz polegać na swoim doświadczeniu. Jeśli nie twoje, to ktoś starszy w domenie. Możesz spierać się o to, jakie są kryteria akceptacji. Jeśli jest to coś w stylu „użytkownik powinien mieć możliwość zmiany typu z„ A ”na„ B ”„ w porównaniu z ”użytkownik powinien mieć możliwość zmiany typu z bieżącej wartości na dowolną dozwoloną wartość alternatywną”.

Często kryteria akceptacji podlegają interpretacji, ale dobry personel QA może napisać kryteria odpowiednie do wykonywanego zadania, minimalizując niezbędną interpretację.

Czy istnieją ograniczenia domeny, które nie pozwalają na zmianę z „A” na „C” lub jakąkolwiek inną opcję, ale tylko „A” na „B”? Czy jest to tylko wąsko określony wymóg, który nie jest „myśleniem przyszłościowym”?

Gdyby ogólny przypadek był trudniejszy, zapytałbym go przed rozpoczęciem pracy, ale w twoim przypadku, gdybym mógł „przewidzieć”, że w przyszłości pojawią się inne wnioski o zmianę typu, byłbym skłonny: a) napisz coś wielokrotnego użytku dla ogólnego przypadku i b) zawiń w warunek, który na razie pozwala tylko A -> B.

Wystarczająco łatwe do zweryfikowania dla bieżącego przypadku w testach automatycznych i wystarczająco łatwe, aby później otworzyć inne opcje, jeśli / kiedy pojawią się różne przypadki użycia.


1

Dla mnie wytyczna, którą ustaliłem jakiś czas temu, brzmi: „Dla hipotetycznych wymagań pisz tylko hipotetyczny kod”. To znaczy - jeśli spodziewasz się dodatkowych wymagań, powinieneś pomyśleć trochę o tym, jak je zaimplementować i ustrukturyzować swój obecny kod, aby nie blokował w ten sposób.

Ale nie pisz teraz dla nich rzeczywistego kodu - pomyśl tylko, co byś zrobił. W przeciwnym razie zwykle będziesz robił rzeczy niepotrzebnie skomplikowanymi i prawdopodobnie zirytujesz się później, gdy pojawią się rzeczywiste wymagania, które różnią się od oczekiwanych.

Cztery twój przykład: jeśli masz pod kontrolą wszystkie zastosowania metody konwersji, możesz po prostu nazwać ją teraz convertAToB i planujesz użyć refaktoryzacji „metody zmiany nazwy” w IDE, aby zmienić jej nazwę, jeśli później potrzebujesz bardziej ogólnej funkcjonalności. Jeśli jednak metoda konwersji jest częścią publicznego interfejsu API, może być zupełnie inaczej: jej specyfikacja zablokowałaby później generalizację, ponieważ w takim przypadku trudno jest zmienić nazwę rzeczy.


0

Ciągle słyszę, że dobry programista powinien starać się przewidzieć zmianę i zaprojektować system, aby można go było łatwo rozbudować w przyszłości,

Zasadniczo tak. Ale to nie nie musi prowadzić do rozwiązań generycznych.

Według mnie istnieją dwa rodzaje tematów związanych z tworzeniem oprogramowania, w których należy przewidzieć przyszłe zmiany:

  • Biblioteki przeznaczone do użytku przez strony trzecie, oraz
  • ogólna architektura oprogramowania.

Pierwszy przypadek rozwiązuje się obserwując spójność / sprzężenie, zastrzyk zależności lub cokolwiek innego. Drugi przypadek jest na bardziej abstrakcyjnym poziomie, na przykład wybranie architektury zorientowanej na usługi zamiast dużego monobotycznego obiektu typu blob dla dużej aplikacji.

W twoim przypadku prosisz o konkretne rozwiązanie konkretnego problemu, który nie ma żadnego przewidywalnego wpływu na przyszłość. W tym przypadku YAGNI i SUCHY to dobre motta do strzelania do:

  • YAGNI (nie będziesz go potrzebować) mówi ci, abyś wdrożył absolutnie minimalne, podstawowe rzeczy, których potrzebujesz i których potrzebujesz teraz . Oznacza to zaimplementowanie minimum, które powoduje, że bieżący zestaw testów zmienia kolor z czerwonego na zielony, jeśli powinieneś używać rozwoju stylu TDD / BDD / FDD. Ani jednej linii więcej.
  • DRY (dry) znaczy, jeśli się o podobny problem ponownie, a następnie podjąć dobrą twarde spojrzenie czy trzeba rodzajowe rozwiązanie.

W połączeniu z innymi nowoczesnymi praktykami (takimi jak dobre pokrycie testowe, aby móc bezpiecznie refaktoryzować) oznacza to, że otrzymujesz szybko napisany, oszczędny, średni kod, który rośnie w miarę potrzeb.

które brzmi jak ogólne rozwiązanie jest właściwą drogą?

Nie, wygląda na to, że powinieneś mieć środowiska programistyczne, języki i narzędzia, dzięki którym refaktoryzacja w razie potrzeby jest łatwa i przyjemna. Ogólne rozwiązania tego nie zapewniają; oddzielają aplikację od rzeczywistej domeny.

Spójrz na nowoczesne frameworki ORM lub MVC, na przykład Ruby on Rails; na poziomie aplikacji wszystko koncentruje się na wykonywaniu pracy innej niż ogólna. Biblioteki same w sobie są oczywiście prawie w 100% ogólne, ale kod domeny (o co chodzi w twoim pytaniu) powinien w tym aspekcie robić minimalne shenanigany.


0

Innym sposobem myślenia o problemie jest rozważenie, co ma sens.

Na przykład opracowywałem aplikację, która zawierała sekcje, które działały prawie tak samo, ale miały niespójne reguły dotyczące zezwoleń. Ponieważ nie było powodu, aby je odróżniać, kiedy ponownie przebudowałem tę sekcję, kazałem im wszystkim robić uprawnienia w ten sam sposób. Dzięki temu cały kod był mniejszy, prostszy, a interfejs był bardziej spójny.

Kiedy zarząd postanowił zezwolić innym osobom na dostęp do funkcji, mogliśmy to zrobić, zmieniając tylko flagę.

Oczywiście sensowne jest dokonanie konwersji określonego typu. Czy sensowne jest także dokonywanie dodatkowych konwersji typu?

Należy pamiętać, że jeśli ogólne rozwiązanie jest szybsze do wdrożenia, to odpowiedni przypadek jest również łatwy, po prostu sprawdź, czy jest to jedyna dozwolona konwersja typu.

Jeśli aplikacja znajduje się w ściśle regulowanym obszarze (aplikacja medyczna lub finansowa), spróbuj zaangażować w projekt więcej osób.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.