Mam na myśli własny biznes w domu, a moja żona podchodzi do mnie i mówi
Kochanie ... Czy możesz wydrukować wszystkie Day Light Oszczędności na całym świecie na rok 2018 w konsoli? Muszę coś sprawdzić.
I jestem bardzo szczęśliwy, ponieważ właśnie z tym doświadczeniem Java czekałem całe życie i wymyśliłem:
import java.time.*;
import java.util.Set;
class App {
void dayLightSavings() {
final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(
zoneId -> {
LocalDateTime dateTime = LocalDateTime.of(
LocalDate.of(2018, 1, 1),
LocalTime.of(0, 0, 0)
);
ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
while (2018 == now.getYear()) {
int hour = now.getHour();
now = now.plusHours(1);
if (now.getHour() == hour) {
System.out.println(now);
}
}
}
);
}
}
Ale potem mówi, że właśnie testowała mnie, czy jestem etycznie wyszkolonym inżynierem oprogramowania, i mówi mi, że wygląda na to, że nie jestem (zabrany stąd ) ..
Należy zauważyć, że żaden etycznie wyszkolony inżynier oprogramowania nigdy nie wyraziłby zgody na napisanie procedury DestroyBaghdad. Podstawowa etyka zawodowa wymagałaby zamiast tego napisania procedury DestroyCity, której Bagdad mógłby zostać podany jako parametr.
I jestem jak, w porządku, ok, dostałeś mnie .. Spędź dowolny rok , jak chcesz, proszę bardzo:
import java.time.*;
import java.util.Set;
class App {
void dayLightSavings(int year) {
final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
availableZoneIds.forEach(
zoneId -> {
LocalDateTime dateTime = LocalDateTime.of(
LocalDate.of(year, 1, 1),
LocalTime.of(0, 0, 0)
);
ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
while (year == now.getYear()) {
// rest is same..
Ale skąd mam wiedzieć, ile (i co) sparametryzować? W końcu mogłaby powiedzieć…
- chce przekazać niestandardowy formatator ciągów, może nie lubi formatu, w którym już drukuję:
2018-10-28T02:00+01:00[Arctic/Longyearbyen]
void dayLightSavings(int year, DateTimeFormatter dtf)
- interesuje się tylko niektórymi miesięcznymi okresami
void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd)
- interesuje się pewnymi przedziałami godzinowymi
void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd, int hourStart, int hourend)
Jeśli szukasz konkretnego pytania:
Jeśli destroyCity(City city)
jest lepszy niż destroyBaghdad()
, czy jest takeActionOnCity(Action action, City city)
jeszcze lepszy? Dlaczego? Dlaczego nie?
Po tym wszystkim, mogę najpierw wywołać ją Action.DESTROY
wtedy Action.REBUILD
, prawda?
Ale podejmowanie działań na miastach nie jest na tyle dla mnie, co powiesz takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)
? W końcu nie chcę dzwonić:
takeActionOnCity(Action.DESTORY, City.BAGHDAD);
następnie
takeActionOnCity(Action.DESTORY, City.ERBIL);
i tak dalej, kiedy mogę:
takeActionOnGeographicArea(Action.DESTORY, Country.IRAQ);
ps Moje pytanie zbudowałem tylko wokół cytowanego przeze mnie cytatu: nie mam nic przeciwko żadnemu krajowi, religii, rasie czy cokolwiek na świecie. Ja tylko próbuję coś wyjaśnić.
źródło
destroyCity(target)
jest o wiele bardziej nieetyczne niżdestroyBagdad()
! Jaki potwór pisze program wymazania miasta, a co dopiero każdego miasta na świecie? Co jeśli system został przejęty ?! Co również zarządzanie czasem / zasobami (zainwestowane wysiłki) ma wspólnego z etyką? O ile umowa ustna / pisemna została zakończona zgodnie z ustaleniami.Odpowiedzi:
Żółwie są na dole.
Lub abstrakcje w tym przypadku.
Kodowanie dobrych praktyk jest czymś, co można zastosować w nieskończoność, a w pewnym momencie abstraktujesz dla samego abstrakcji, co oznacza, że posunąłeś się za daleko. Znalezienie tej linii nie jest czymś łatwym do zastosowania, ponieważ zależy od twojego środowiska.
Na przykład mieliśmy klientów, którzy wcześniej prosili o proste aplikacje, a potem o rozszerzenia. Mieliśmy również klientów, którzy pytają, czego chcą i na ogół nigdy nie wracają do nas w celu rozszerzenia.
Twoje podejście będzie się różnić w zależności od klienta. Pierwszy klient zapłaci za uprzedzające wyodrębnienie kodu, ponieważ masz całkowitą pewność, że w przyszłości będziesz musiał go ponownie odwiedzić. W przypadku drugiego klienta możesz nie chcieć zainwestować dodatkowego wysiłku, jeśli oczekujesz, że nie będzie chciał rozszerzyć aplikacji w żadnym momencie (uwaga: nie oznacza to, że nie przestrzegasz żadnej dobrej praktyki, ale po prostu że unikasz robienia więcej niż jest to obecnie konieczne.
Skąd mam wiedzieć, które funkcje wdrożyć?
Powodem, dla którego wspominam powyżej, jest to, że już wpadłeś w tę pułapkę:
„Może powiedzieć” nie jest aktualnym wymogiem biznesowym. Odgadnij przyszłe wymagania biznesowe. Zasadniczo nie opieraj się na domysłach, opracowuj tylko to, co jest obecnie wymagane.
Jednak kontekst ma tutaj zastosowanie. Nie znam twojej żony. Może dokładnie oceniłeś, że tak naprawdę będzie tego chciała. Ale nadal powinieneś potwierdzić z klientem, że to jest właśnie to, czego on chce, ponieważ w przeciwnym razie poświęcisz czas na opracowanie funkcji, z której nigdy nie skorzystasz.
Skąd mam wiedzieć, którą architekturę wdrożyć?
To trudniejsze. Klient nie dba o wewnętrzny kod, więc nie możesz zapytać go, czy go potrzebuje. Ich opinia w tej sprawie jest w większości nieistotna.
Nadal jednak możesz to potwierdzić, zadając odpowiednie pytania klientowi. Zamiast pytać o architekturę, zapytaj ich o ich oczekiwania dotyczące przyszłego rozwoju lub rozszerzenia bazy kodu. Możesz również zapytać, czy aktualny cel ma wyznaczony termin, ponieważ możesz nie być w stanie wdrożyć fantazyjnej architektury w niezbędnym czasie.
Skąd mam wiedzieć, kiedy jeszcze bardziej wyodrębnić kod?
Nie wiem, gdzie to czytam (jeśli ktoś wie, daj mi znać, a dam kredyt), ale dobrą zasadą jest, że programiści powinni liczyć się jak jaskiniowiec: raz, dwa, wiele .
XKCD # 764
Innymi słowy, gdy jakiś algorytm / wzorzec zostanie użyty po raz trzeci , należy go wyodrębnić, aby można go było ponownie wykorzystać (= użyteczny wiele razy).
Żeby było jasne, nie sugeruję, że nie powinieneś pisać kodu wielokrotnego użytku, gdy używane są tylko dwa wystąpienia algorytmu. Oczywiście możesz to również wyodrębnić, ale reguła powinna być taka, że w trzech przypadkach musisz wyabstrahować.
Ponownie, to wpływa na twoje oczekiwania. Jeśli już wiesz, że potrzebujesz trzech lub więcej instancji, oczywiście możesz natychmiast odreagować. Ale jeśli tylko zgadniesz, że możesz chcieć zaimplementować go więcej razy, poprawność implementacji abstrakcji w pełni zależy od poprawności twojego przypuszczenia.
Jeśli zgadłeś poprawnie, zaoszczędziłeś trochę czasu. Jeśli źle zgadłeś, zmarnowałeś trochę czasu i wysiłku i być może naraziłeś swoją architekturę na wdrożenie czegoś, czego w końcu nie potrzebujesz.
To bardzo zależy od wielu rzeczy:
takeActionOnCity
metodzie.Pamiętaj też, że jeśli rekurencyjnie to abstrakcyjnie skończysz, uzyskasz metodę tak abstrakcyjną, że uruchomisz inną metodę tylko w kontenerze, co oznacza, że Twoja metoda stała się nieistotna i bez znaczenia.
Jeśli całe
takeActionOnCity(Action action, City city)
ciało metody kończy się niczym więcejaction.TakeOn(city);
, powinieneś się zastanowić, czytakeActionOnCity
metoda ma naprawdę cel, czy nie jest tylko dodatkową warstwą, która nie wnosi nic wartościowego.To samo pytanie pojawia się tutaj:
Jeśli możesz definitywnie odpowiedzieć „tak” na wszystkie trzy, uzasadniona jest abstrakcja.
źródło
Ćwiczyć
To jest Software Engineering SE, ale oprogramowanie do tworzenia to o wiele więcej sztuki niż inżynieria. Nie ma uniwersalnego algorytmu do naśladowania ani pomiaru, aby ustalić, ile wystarczy ponownego użycia. Jak w przypadku wszystkiego, im więcej ćwiczysz projektowania programów, tym lepiej sobie z tym poradzisz. Lepiej poczujesz, co jest „wystarczające”, ponieważ zobaczysz, co idzie nie tak i jak idzie źle, gdy sparametryzujesz za dużo lub za mało.
Nie jest to teraz zbyt pomocne , więc co powiesz na jakieś wytyczne?
Spójrz na swoje pytanie. Jest wiele „mogła powiedzieć” i „mógłbym”. Wiele wypowiedzi teoretycznych na temat niektórych przyszłych potrzeb. Ludzie nie znają przewidywać przyszłości. A ty (najprawdopodobniej) jesteś człowiekiem. Przytłaczającym problemem związanym z projektowaniem oprogramowania jest próba wyjaśnienia przyszłości, której nie znasz.
Wytyczna 1: Nie będziesz go potrzebować
Poważnie. Przestań. Najczęściej wyobrażony problem przyszłości nie pojawia się - i na pewno nie pojawi się tak, jak sobie wyobrażałeś.
Wytyczna 2: Koszt / korzyść
Fajnie, ten mały program zajął ci kilka godzin. Więc co jeśli żona ma wrócić i poprosić o tych rzeczach? W najgorszym przypadku poświęcasz kilka godzin na zebranie innego programu, aby to zrobić. W tym przypadku nie jest zbyt wiele czasu, aby ten program był bardziej elastyczny. Nie wpłynie to znacząco na szybkość działania ani zużycie pamięci. Ale nietrywialne programy mają różne odpowiedzi. Różne scenariusze mają różne odpowiedzi. W pewnym momencie koszty wyraźnie nie są warte korzyści, nawet przy niedoskonałych umiejętnościach mówienia w przyszłości.
Wytyczna 3: Koncentracja na stałych
Spójrz na pytanie. W twoim oryginalnym kodzie jest wiele stałych int.
2018
,1
. Stałe ints, stałe ciągi ... Są to najbardziej prawdopodobne rzeczy, które powinny być niestałe . Co więcej, parametryzacja zajmuje im tylko trochę czasu (lub przynajmniej definiuje jako rzeczywiste stałe). Ale inną rzeczą, na którą należy uważać, jest ciągłe zachowanie . TheSystem.out.println
na przykład. Takie założenie dotyczące użytkowania jest czymś, co zmienia się w przyszłości i jest bardzo kosztowne do naprawienia. Nie tylko to, ale takie IO sprawia, że funkcja jest nieczysta (wraz z nieco pobieraną strefą czasową). Parametryzacja tego zachowania może uczynić funkcję bardziej czystą, co prowadzi do większej elastyczności i testowalności. Duże korzyści przy minimalnych kosztach (zwłaszcza, jeśli przeciążenie jest używaneSystem.out
domyślnie).źródło
Po pierwsze: żaden programista zorientowany na bezpieczeństwo nie napisałby metody DestroyCity bez podania tokena autoryzacji z jakiegokolwiek powodu.
Ja też mogę napisać wszystko jako imperatyw, który ma oczywistą mądrość, ale nie można go zastosować w innym kontekście. Dlaczego konieczne jest autoryzowanie konkatenacji ciągu?
Po drugie: cały kod po uruchomieniu musi być w pełni określony .
Nie ma znaczenia, czy decyzja została na stałe zakodowana na miejscu, czy odroczona na inną warstwę. W pewnym momencie jest jakiś kod w jakimś języku, który wie zarówno, co ma zostać zniszczone, jak i instrukcje.
Może to być ten sam plik obiektowy
destroyCity(xyz)
i może to być plik konfiguracyjny:destroy {"city": "XYZ"}"
lub może to być seria kliknięć i naciśnięć klawiszy w interfejsie użytkownika.Po trzecie:
to zupełnie inny zestaw wymagań do:
Teraz drugi zestaw wymagań oczywiście zapewnia bardziej elastyczne narzędzie. Ma szerszą grupę docelową i szerszą dziedzinę zastosowań. Niebezpieczeństwo polega na tym, że najbardziej elastyczną aplikacją na świecie jest w rzeczywistości kompilator kodu maszynowego. Jest to dosłownie program tak ogólny, że może zbudować wszystko, aby uczynić komputer jakimkolwiek jest (w ramach ograniczeń sprzętowych).
Ogólnie rzecz biorąc, ludzie, którzy potrzebują oprogramowania, nie chcą czegoś ogólnego; chcą czegoś konkretnego. Dając więcej opcji, w rzeczywistości komplikujesz ich życie. Gdyby chcieli tej złożoności, zamiast tego użyliby kompilatora, nie pytając.
Twoja żona prosiła o funkcjonalność i nie spełniła twoich wymagań. W tym przypadku było to pozornie celowe, a ogólnie dlatego, że nie wiedzą nic lepszego. W przeciwnym razie po prostu skorzystaliby z kompilatora. Pierwszym problemem jest to, że nie poprosiłeś o więcej szczegółów na temat tego, co dokładnie chciała zrobić. Czy chciała to uruchomić przez kilka różnych lat? Czy chciała tego w pliku CSV? Nie dowiedziałeś się, jakie decyzje chce sama podjąć, i o co poprosiła cię, abyś wymyślił i zdecydował za nią. Po ustaleniu, jakie decyzje należy odroczyć, możesz dowiedzieć się, jak przekazać te decyzje za pomocą parametrów (i innych konfigurowalnych środków).
To powiedziawszy, większość klientów nie komunikuje się, nie zakłada lub nie wie o pewnych szczegółach (czyli decyzjach), które naprawdę chcieliby sami zrobić, lub których tak naprawdę nie chcą (ale brzmi to niesamowicie). Dlatego ważne są metody pracy takie jak PDSA (plan-opracowanie-badanie-akt). Zaplanowałeś pracę zgodnie z wymaganiami, a następnie opracowałeś zestaw decyzji (kod). Teraz nadszedł czas, aby przestudiować go, samodzielnie lub z klientem, i nauczyć się nowych rzeczy, a te informują o dalszym myśleniu. Wreszcie działaj zgodnie ze swoimi nowymi spostrzeżeniami - zaktualizuj wymagania, dopracuj proces, zdobądź nowe narzędzia itp. Następnie zacznij planowanie ponownie. To z czasem ujawniłoby wszelkie ukryte wymagania i świadczy o postępie wielu klientów.
Wreszcie. Twój czas jest ważny ; jest bardzo realny i bardzo skończony. Każda twoja decyzja pociąga za sobą wiele innych ukrytych decyzji i na tym właśnie polega tworzenie oprogramowania. Opóźnienie decyzji jako argumentu może uprościć bieżącą funkcję, ale sprawia, że gdzie indziej staje się bardziej złożona. Czy ta decyzja jest istotna w tej innej lokalizacji? Czy jest to bardziej odpowiednie tutaj? Czyją decyzję należy podjąć? Ty decydujesz o tym; to jest kodowanie. Jeśli często powtarzasz zestawy decyzji, bardzo realna jest kodyfikacja ich w ramach abstrakcji. XKCD ma tutaj przydatną perspektywę. Ma to znaczenie na poziomie systemu, czy to funkcji, modułu, programu itp.
Porady na początku sugerują, że decyzje, do których Twoja funkcja nie ma prawa podejmować, powinny być przekazywane jako argument. Problem polega na tym, że
DestroyBaghdad
funkcja może być funkcją, która ma to prawo.źródło
Jest tu wiele długich odpowiedzi, ale szczerze mówiąc, myślę, że to bardzo proste
więc w twojej funkcji
Ty masz:
Więc przeniósłbym to wszystko do parametrów w takiej czy innej formie. Można argumentować, że zoneIds są niejawne w nazwie funkcji, być może chciałbyś uczynić to jeszcze bardziej, zmieniając ją na „DaylightSavingsAroundTheWorld” lub coś w tym rodzaju
Nie masz ciągu formatu, więc dodanie jednego jest żądaniem funkcji i powinieneś skierować swoją żonę do instancji rodziny Jira. Można go zaliczyć do zaległości i ustalić priorytet na odpowiednim posiedzeniu komitetu zarządzającego projektem.
źródło
Krótko mówiąc, nie konstruuj swojego oprogramowania pod kątem możliwości ponownego użycia, ponieważ żaden użytkownik końcowy nie dba o to, czy twoje funkcje mogą być ponownie użyte. Zamiast tego inżynier zrozumiałości projektu - czy mój kod jest łatwy do zrozumienia dla kogoś innego lub dla mojej przyszłej zapomnianej osoby? - i elastyczność projektowania- kiedy nieuchronnie muszę naprawić błędy, dodać funkcje lub w inny sposób zmodyfikować funkcjonalność, w jakim stopniu mój kod będzie odporny na zmiany? Jedyne, na czym zależy Twojemu klientowi, to jak szybko możesz odpowiedzieć, kiedy zgłosi błąd lub poprosi o zmianę. Nawiasem mówiąc, zadawanie tych pytań na temat projektu powoduje, że kod nadaje się do ponownego użycia, ale takie podejście pozwala skupić się na unikaniu prawdziwych problemów, które napotkasz w ciągu całego życia tego kodu, dzięki czemu możesz lepiej służyć użytkownikowi końcowemu, niż zajmować się wzniosłym, niepraktycznym „inżynieryjne” ideały zadowolenia karku.
W przypadku czegoś tak prostego, jak podany przykład, początkowa implementacja jest dobra ze względu na to, jak niewielka jest, ale ten prosty projekt będzie trudny do zrozumienia i kruchy, jeśli spróbujesz wcisnąć zbyt dużą elastyczność funkcjonalną (w przeciwieństwie do elastyczności projektowania) w jedna procedura. Poniżej znajduje się wyjaśnienie mojego preferowanego podejścia do projektowania złożonych systemów pod kątem zrozumiałości i elastyczności, które mam nadzieję pokażą, co mam na myśli. Nie zastosowałbym tej strategii do czegoś, co można by napisać w mniej niż 20 wierszach w jednej procedurze, ponieważ coś tak małego już spełnia moje kryteria zrozumiałości i elastyczności.
Przedmioty, a nie Procedury
Zamiast używać klas takich jak oldskulowe moduły z szeregiem wywoływanych procedur w celu wykonywania czynności, które powinno robić oprogramowanie, rozważ modelowanie domeny jako obiektów, które oddziałują na siebie i współpracują w celu wykonania danego zadania. Metody w paradygmacie zorientowanym obiektowo zostały pierwotnie stworzone, aby były sygnałami między obiektami, dzięki czemu
Object1
mogły powiedzieć,Object2
że robią coś, cokolwiek to jest, i być może otrzymać sygnał zwrotny. Wynika to z faktu, że paradygmat zorientowany obiektowo polega na modelowaniu obiektów domeny i ich interakcji, a nie na wymyślnym sposobie organizowania tych samych starych funkcji i procedur paradygmatu imperatywnego. W przypadkuvoid destroyBaghdad
na przykład, zamiast próbować pisać bezkontekstową ogólną metodę radzenia sobie ze zniszczeniem Bagdadu lub jakiejkolwiek innej rzeczy (która może szybko stać się złożona, trudna do zrozumienia i krucha), każda rzecz, która może zostać zniszczona, powinna być odpowiedzialna za zrozumienie, w jaki sposób zniszczyć się. Na przykład masz interfejs opisujący zachowanie rzeczy, które można zniszczyć:Masz miasto, które implementuje ten interfejs:
Nic, co wymaga zniszczenia instancji
City
, nigdy nie będzie miało znaczenia , jak to się stanie, więc nie ma powodu, aby ten kod istniał gdziekolwiek pozaCity::destroy
, a rzeczywiście, intymna znajomość wewnętrznych mechanizmów działaniaCity
samego siebie byłaby ścisłym sprzężeniem, które zmniejsza elastyczność, ponieważ musisz wziąć pod uwagę te elementy zewnętrzne, jeśli kiedykolwiek będziesz musiał zmodyfikować zachowanieCity
. To jest prawdziwy cel enkapsulacji. Pomyśl o tym, jakby każdy obiekt miał swój własny interfejs API, który powinien umożliwić ci zrobienie z nim wszystkiego, co musisz, abyś mógł się martwić spełnieniem twoich żądań.Delegacja, a nie „kontrola”
Teraz, czy twoja klasa wdrażająca jest,
City
czyBaghdad
zależy od tego, jak ogólny jest proces niszczenia miasta. Najprawdopodobniej aCity
będzie składać się z mniejszych elementów, które będą musiały zostać zniszczone indywidualnie, aby doprowadzić do całkowitego zniszczenia miasta, więc w takim przypadku każdy z tych elementów również się wdrożyDestroyable
, i każdy z nich zostanie poinstruowany,City
aby zniszczyć sami w taki sam sposób, jak ktoś z zewnątrz poprosiłCity
o samozniszczenie.Jeśli chcesz naprawdę oszaleć i wdrożyć ideę
Bomb
upuszczenia na lokalizację i niszczy wszystko w określonym promieniu, może to wyglądać mniej więcej tak:ObjectsByRadius
reprezentuje zestaw obiektów, który jest obliczany na podstawieBomb
danych wejściowych, ponieważBomb
nie ma znaczenia, w jaki sposób obliczenia są wykonywane, o ile może on działać z obiektami. Nawiasem mówiąc, jest to wielokrotnego użytku, ale głównym celem jest odizolowanie obliczeń od procesów upuszczaniaBomb
i niszczenia obiektów, abyś mógł zrozumieć każdy element i sposób, w jaki pasują do siebie, i zmienić zachowanie pojedynczego elementu bez konieczności przekształcania całego algorytmu .Interakcje, nie algorytmy
Zamiast próbować odgadnąć odpowiednią liczbę parametrów dla złożonego algorytmu, bardziej sensowne jest modelowanie procesu jako zestawu interaktywnych obiektów, z których każdy ma niezwykle wąskie role, ponieważ daje to możliwość modelowania złożoności twojego przetwarzaj przez interakcje między tymi dobrze zdefiniowanymi, łatwymi do zrozumienia i prawie niezmiennymi obiektami. Prawidłowe wykonanie sprawia, że nawet niektóre z najbardziej złożonych modyfikacji są tak trywialne, jak implementacja jednego lub dwóch interfejsów i przerobienie, które obiekty są tworzone w twojej
main()
metodzie.Dałbym ci coś do twojego oryginalnego przykładu, ale szczerze mówiąc, nie mogę zrozumieć, co to znaczy „wydrukować ... Oszczędności na światło dzienne”. Co mogę powiedzieć o tej kategorii problemu, to to, że za każdym razem, gdy wykonujesz obliczenia, których wynik można sformatować na wiele sposobów, mój preferowany sposób na rozbicie tego wygląda następująco:
Ponieważ twój przykład używa klas z biblioteki Java, które nie obsługują tego projektu, możesz po prostu użyć interfejsu API
ZonedDateTime
bezpośrednio. Chodzi o to, że każde obliczenie jest zawarte w swoim własnym obiekcie. Nie przyjmuje żadnych założeń co do tego, ile razy powinien działać i jak sformatować wynik. Dotyczy wyłącznie wykonywania najprostszej formy obliczeń. To sprawia, że jest łatwy do zrozumienia i elastyczny w zmianie. PodobnieResult
dotyczy wyłącznie enkapsulacji wyniku obliczeń, aFormattedResult
dotyczy wyłącznie interakcji zResult
formatowaniem zgodnie z określonymi przez nas regułami. W ten sposób,możemy znaleźć idealną liczbę argumentów dla każdej z naszych metod, ponieważ każda z nich ma dobrze zdefiniowane zadanie . O wiele łatwiej jest modyfikować poruszanie się do przodu, o ile interfejsy się nie zmieniają (co nie jest tak prawdopodobne, jeśli odpowiednio zminimalizujesz odpowiedzialność za swoje obiekty). Naszamain()
metoda może wyglądać następująco:W rzeczywistości programowanie zorientowane obiektowo zostało wymyślone specjalnie jako rozwiązanie problemu złożoności / elastyczności paradygmatu imperatywnego, ponieważ nie ma po prostu dobrej odpowiedzi (na którą każdy może się zgodzić lub niezależnie), jak optymalnie określić funkcje imperatywne i procedury w ramach tego idiomu.
źródło
Doświadczenie , wiedza o domenach i recenzje kodów.
I niezależnie od tego, ile masz doświadczenia , wiedzy na temat domeny lub zespołu , nie możesz uniknąć konieczności refaktoryzacji w razie potrzeby.
Dzięki Experience zaczniesz rozpoznawać wzorce w metodach (i klasach) niespecyficznych dla domeny, które piszesz. A jeśli w ogóle interesujesz się kodem DRY, poczujesz złe uczucia, gdy napotkasz metodę, o której instynktownie wiesz , że napiszesz różne wersje w przyszłości. Zatem zamiast tego intuicyjnie napiszesz sparametryzowany najmniej powszechny mianownik.
(To doświadczenie może instynktownie przenieść się na niektóre obiekty i metody domeny).
Dzięki Domain Knowledge masz poczucie, z którymi koncepcjami biznesowymi są ściśle powiązane, które mają zmienne, które są dość statyczne itp.
Dzięki przeglądom kodu niedostateczna i nadmierna parametryzacja będzie częściej wychwytywana, zanim stanie się kodem produkcyjnym, ponieważ Twoi rówieśnicy (miejmy nadzieję) będą mieli unikalne doświadczenia i perspektywy, zarówno w dziedzinie, jak i ogólnie w kodowaniu.
To powiedziawszy, nowi programiści zazwyczaj nie będą mieli tych Spidey Senses ani doświadczonej grupy rówieśników, na których mogliby się oprzeć. Nawet doświadczeni programiści korzystają z podstawowej dyscypliny, która poprowadzi ich przez nowe wymagania - lub przez mgliste dni. Oto, co zasugeruję na początek :
(Uwzględnij wszelkie parametry, które już wiesz, że będą potrzebne, oczywiście ...)
Kroki te niekoniecznie występują w podanej kolejności. Jeśli usiądziesz, aby napisać metodę, o której wiesz, że jest wysoce zbędna w stosunku do istniejącej metody , przejdź od razu do refaktoryzacji, jeśli jest to wygodne. (Jeśli refaktoryzacja nie zajmie znacznie więcej czasu niż byłoby po prostu napisać, przetestować i utrzymać dwie metody).
Ale oprócz tego, że mam dużo doświadczenia i tak dalej, radzę dość minimalistycznym OSUSZANIEM kodu. Refaktoryzacja oczywistych naruszeń nie jest trudna. A jeśli jesteś zbyt gorliwy , możesz skończyć z kodem „nadmiernie przesuszonym”, który jest jeszcze trudniejszy do odczytania, zrozumienia i utrzymania niż ekwiwalent „WET”.
źródło
If destoryCity(City city) is better than destoryBaghdad(), is takeActionOnCity(Action action, City city) even better?
? To pytanie tak / nie, ale nie ma odpowiedzi, prawda? Czy początkowe założenie jest błędne,destroyCity(City)
niekoniecznie musi być lepsze i tak naprawdę zależy ? Więc to nie znaczy, że nie jestem niewykształconym etycznie inżynierem oprogramowania, ponieważ bezpośrednio wdrożyłem bez żadnych parametrów? Mam na myśli jaka jest odpowiedź na konkretne pytanie, które zadaję?destroyBaghdad()
metody. Jaki jest kontekst? Czy to gra wideo, w której koniec gry powoduje zniszczenie Bagdadu? Jeśli tak, todestroyBaghdad()
może być całkowicie rozsądna nazwa / podpis metody ...It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure.
Gdybyś był w pokoju z Nathanielem Borensteinem, kłóciłbyś się z nim, że tak naprawdę zależy, a jego wypowiedź jest nieprawidłowa? To znaczy, że to piękne, że wiele osób odpowiada, spędzając czas i energię, ale nigdzie nie widzę żadnej konkretnej odpowiedzi. Spidey-zmysły, recenzje kodu .. Ale na co jest odpowiedźis takeActionOnCity(Action action, City city) better?
null
?Ta sama odpowiedź, co w przypadku jakości, użyteczności, zadłużenia technicznego itp .:
Jako wielokrotnego użytku, jak ty, użytkownik, 1 trzeba je będzie
Zasadniczo jest to wezwanie do oceny - czy koszt zaprojektowania i utrzymania abstrakcji zostanie zwrócony przez koszt (= czas i wysiłek), to cię uratuje.
1 To jest konstrukcja kodu, więc w tym przypadku jesteś „użytkownikiem” - użytkownikiem kodu źródłowego
źródło
Istnieje przejrzysty proces, który można wykonać:
Wynika to z - przynajmniej zdaniem niektórych osób - dość optymalnego kodu, ponieważ jest tak mały, jak to możliwe, każda ukończona funkcja zajmuje tak mało czasu, jak to możliwe (co może, ale nie musi być prawdą, jeśli spojrzysz na gotowy produkt po refaktoryzacji) i ma bardzo dobre pokrycie testowe. W zauważalny sposób unika się również zbyt ogólnych metod lub klas.
Daje to również bardzo jasne instrukcje, kiedy należy uogólniać, a kiedy specjalizować.
Uważam przykład twojego miasta za dziwny; Prawdopodobnie nigdy nie zapisałbym na stałe nazwy miasta. Jest tak oczywiste, że dodatkowe miasta zostaną uwzględnione później, bez względu na to, co robisz. Ale innym przykładem mogą być kolory. W niektórych okolicznościach możliwe byłoby zakodowanie na czerwono lub zielono. Na przykład sygnalizacja świetlna jest tak wszechobecnym kolorem, że można po prostu uciec (i zawsze można zrefaktować). Różnica polega na tym, że „czerwony” i „zielony” mają uniwersalne, „zakodowane na stałe” znaczenie w naszym świecie, jest niewiarygodnie mało prawdopodobne, że kiedykolwiek się zmieni, i nie ma tak naprawdę alternatywy.
Twoja pierwsza metoda oszczędzania światła dziennego jest po prostu zepsuta. Mimo że jest zgodny ze specyfikacjami, zakodowany w 2018 r. Jest szczególnie zły, ponieważ a) nie jest wymieniony w „umowie” technicznej (w tym przypadku w metodzie), oraz b) wkrótce będzie nieaktualny, więc pęknięcie jest wliczony od samego początku. W przypadku rzeczy związanych z datą / godziną bardzo rzadko sensowne jest kodowanie konkretnej wartości, ponieważ czas płynie. Ale poza tym wszystko inne jest przedmiotem dyskusji. Jeśli podasz prosty rok, a następnie zawsze obliczysz cały rok, śmiało. Większość rzeczy, które wymieniłeś (formatowanie, wybór mniejszego zakresu itp.) Krzyczy, że twoja metoda robi zbyt wiele, i zamiast tego prawdopodobnie powinna zwrócić listę / tablicę wartości, aby osoba dzwoniąca mogła sama przeprowadzić formatowanie / filtrowanie.
Ale pod koniec dnia większość z nich to opinia, smak, doświadczenie i osobiste nastawienie, więc nie przejmuj się tym zbytnio.
źródło
Doszedłem do wniosku, że istnieją dwa rodzaje kodu wielokrotnego użytku:
Pierwszy rodzaj ponownego użycia jest często dobrym pomysłem. Odnosi się do rzeczy takich jak listy, mapy skrótów, magazyny kluczy / wartości, dopasowania łańcuchów (np. Regex, glob, ...), krotki, unifikacja, drzewa wyszukiwania (najpierw głębokość, najpierw szerokość, pogłębianie iteracyjne, ...) , kombinatory parsera, pamięci podręczne / moduły pamięci, czytniki / moduły zapisujące dane (wyrażenia s, XML, JSON, protobuf, ...), kolejki zadań itp.
Te rzeczy są tak ogólne, w bardzo abstrakcyjny sposób, że są ponownie używane w każdym miejscu w codziennym programowaniu. Jeśli zauważysz, że piszesz kod specjalnego przeznaczenia, który byłby prostszy, gdyby był bardziej abstrakcyjny / ogólny (np. Jeśli mamy „listę zamówień klientów”, możemy wyrzucić rzeczy „zamówienia klientów”, aby uzyskać „listę” ), może to być dobry pomysł, aby to wyciągnąć. Nawet jeśli nie zostanie ponownie użyty, pozwala nam oddzielić niezwiązaną funkcjonalność.
Drugi rodzaj polega na tym, że mamy jakiś konkretny kod, który rozwiązuje prawdziwy problem, ale robi to poprzez podejmowanie wielu decyzji. Możemy uczynić go bardziej ogólnym / wielokrotnego użytku poprzez „miękkie kodowanie” tych decyzji, np. Przekształcanie ich w parametry, komplikowanie implementacji i pieczenie w jeszcze bardziej konkretnych szczegółach (tj. Wiedzy o tym, jakich haków możemy chcieć w celu zastąpienia). Twój przykład wydaje się być tego rodzaju. Problem z tego rodzaju możliwością ponownego użycia polega na tym, że możemy skończyć z odgadywaniem przypadków użycia innych ludzi lub nas samych w przyszłości. W końcu możemy mieć tak wiele parametrów, że nasz kod nie będzie użyteczny, nie mówiąc już o wielokrotnym użyciu! Innymi słowy, dzwonienie wymaga więcej wysiłku niż napisanie własnej wersji. W tym miejscu ważny jest YAGNI (Nie potrzebujesz go). Wiele razy takie próby „wielokrotnego użytku” kodu nie są ponownie wykorzystywane, ponieważ mogą być one niezgodne z tymi przypadkami użycia bardziej fundamentalnie, niż mogą to wyjaśnić parametry, lub ci potencjalni użytkownicy wolą rzucić własne (cholera, spójrz na wszystkie standardy i biblioteki, których autorzy poprzedzili słowo „Simple”, aby odróżnić je od poprzedników!).
Ta druga forma „wielokrotnego użytku” powinna być zasadniczo wykonywana w miarę potrzeb. Jasne, możesz tam umieścić pewne „oczywiste” parametry, ale nie próbuj przewidywać przyszłości. YAGNI.
źródło
destroyBaghdad
jest to jednorazowy skrypt (a przynajmniej idempotentny). Może zniszczenie dowolnego miasta byłoby ulepszeniem, ale co, jeśli udadestroyBaghdad
się zalać Tygrys? Może to być wielokrotnego użytku dla Mosulu i Basry, ale nie dla Mekki i Atlanty.static
metod), które są czysto funkcjonalne i na niskim poziomie, i w przeciwieństwie do tego, decydowanie o „konfigurowaniu parametrów i zaczepów” jest zwykle tym, gdzie musisz zbudować pewne struktury, które wymagają uzasadnienia.Istnieje już wiele doskonałych i skomplikowanych odpowiedzi. Niektóre z nich zagłębiają się w szczegółowe szczegóły, przedstawiają pewne poglądy na temat metodologii tworzenia oprogramowania w ogóle, a niektóre z nich z pewnością zawierają kontrowersyjne elementy lub „opinie”.
W odpowiedzi Warbo wskazano już na różne rodzaje ponownego użycia. Mianowicie, czy coś nadaje się do ponownego użycia, ponieważ jest podstawowym elementem składowym, czy też coś może być ponownie użyte, ponieważ jest w jakiś sposób „ogólne”. Odnosząc się do tego ostatniego, nie jest czymś, co Pomyślę jak jakiegoś środka do ponownego użycia:
Czy jedna metoda może naśladować inną.
Jeśli chodzi o przykład z pytania: Wyobraź sobie, że metoda
była implementacja funkcjonalności, której zażądał klient. Będzie to więc coś, z czego inni programiści powinni korzystać , a zatem metoda publiczna , jak w
public
void dayLightSavings()
Można to wdrożyć, jak pokazano w odpowiedzi. Teraz ktoś chce sparametryzować go z rokiem. Możesz więc dodać metodę
public
void dayLightSavings(int year)
i zmień oryginalną implementację na po prostu
Kolejne „żądania funkcji” i uogólnienia są zgodne z tym samym schematem. Więc jeśli i tylko wtedy, gdy istnieje zapotrzebowanie na postaci najbardziej rodzajowego, można go realizować, wiedząc, że to najbardziej generic forma pozwala na trywialne implementacji bardziej konkretnych z nich:
Jeśli spodziewałeś się przyszłych rozszerzeń i próśb o funkcje, a miałeś do dyspozycji trochę czasu i chciałeś spędzić nudny weekend z (potencjalnie bezużytecznymi) uogólnieniami, od samego początku mogłeś zacząć od najbardziej ogólnego. Ale tylko jako metoda prywatna . Tak długo, jak ujawniłeś tylko prostą metodę, której zażądał klient jako metodę publiczną , jesteś bezpieczny.
tl; dr :
Pytanie w rzeczywistości nie polega na tym, „jak powinna być metoda wielokrotnego użytku”. Pytanie brzmi, jak duża część tego ponownego użycia jest ujawniona i jak wygląda interfejs API. Stworzenie niezawodnego API, który wytrzyma próbę czasu (nawet jeśli pojawią się dalsze wymagania) jest sztuką i rzemiosłem, a temat jest zbyt skomplikowany, aby go tutaj opisać. Na początek spójrz na tę prezentację Joshua Blocha lub wiki z książki projektowej API .
źródło
dayLightSavings()
dzwonieniedayLightSavings(2018)
nie wydaje mi się dobrym pomysłem.dayLightSavings(computeCurrentYear());
. ...Dobrą zasadą jest: twoja metoda powinna być tak samo przydatna jak… wielokrotnego użytku.
Jeśli spodziewasz się, że będziesz wywoływać metodę tylko w jednym miejscu, powinna ona mieć tylko parametry znane stronie wywołującej i niedostępne dla tej metody.
Jeśli masz więcej dzwoniących, możesz wprowadzić nowe parametry, o ile inni dzwoniący mogą przekazać te parametry; w przeciwnym razie potrzebujesz nowej metody.
Ponieważ liczba dzwoniących może rosnąć w czasie, musisz być przygotowany na refaktoryzację lub przeładowanie. W wielu przypadkach oznacza to, że powinieneś czuć się bezpiecznie, wybierając wyrażenie i uruchamiając akcję „wyodrębnij parametr” swojego IDE.
źródło
Bardzo krótka odpowiedź: im mniej sprzężenia lub zależności od innego kodu, który ma moduł ogólny, tym bardziej może być wielokrotnego użytku.
Twój przykład zależy tylko od
więc teoretycznie może być wysoce wielokrotnego użytku.
W praktyce nie sądzę, że kiedykolwiek będziesz mieć drugą skrzynkę użytkową, która potrzebuje tego kodu, więc zgodnie z zasadą yagni nie uczynię go wielokrotnego użytku, jeśli nie będzie więcej niż 3 różne projekty, które potrzebują tego kodu.
Inne aspekty ponownego użycia to łatwość użycia i dokumentacja, które są powiązane z Test Driven Development : Jest to przydatne, jeśli masz prosty test jednostkowy, który demonstruje / dokumentuje łatwe użycie twojego ogólnego modułu jako przykładu kodowania dla użytkowników twojej biblioteki lib.
źródło
To dobra okazja, aby podać regułę, którą ostatnio wymyśliłem:
Bycie dobrym programistą oznacza umiejętność przewidywania przyszłości.
Oczywiście jest to absolutnie niemożliwe! W końcu nigdy nie wiadomo na pewno, jakie uogólnienia okażą się przydatne później, jakie pokrewne zadania będziesz chciał wykonać, jakie nowe funkcje będą chcieli Twoi użytkownicy, i c. Ale doświadczenie czasami daje ogólne pojęcie o tym, co może się przydać.
Inne czynniki, z którymi musisz się zrównoważyć, to ilość dodatkowego czasu i wysiłku oraz o ile bardziej skomplikowany byłby twój kod. Czasami masz szczęście, a rozwiązanie bardziej ogólnego problemu jest w rzeczywistości prostsze! (Przynajmniej koncepcyjnie, jeśli nie w ilości kodu.) Ale częściej koszty komplikacji, a także czasu i wysiłku.
Więc jeśli uważasz, że uogólnienie jest bardzo potrzebne, często warto to zrobić (chyba że doda to dużo pracy lub złożoności); ale jeśli wydaje się to znacznie mniej prawdopodobne, to prawdopodobnie nie jest (chyba że jest to bardzo łatwe i / lub upraszcza kod).
(Na przykład: w zeszłym tygodniu otrzymałem specyfikację dla działań, które system powinien podjąć dokładnie 2 dni po tym, jak coś wygasło. Oczywiście ustawiłem 2-dniowy okres jako parametr. W tym tygodniu ludzie biznesu byli zachwyceni, ponieważ właśnie miałem prosić o to ulepszenie! Miałem szczęście: była to łatwa zmiana i domyśliłem się, że prawdopodobnie będzie ona pożądana. Często trudniej jest ją ocenić. Ale nadal warto próbować przewidzieć, a doświadczenie jest często dobrym przewodnikiem .)
źródło
Po pierwsze, najlepszą odpowiedzią na pytanie „Skąd mam wiedzieć, jak powinny być używane moje metody?” Jest „doświadczenie”. Zrób to kilka tysięcy razy, a zazwyczaj uzyskasz właściwą odpowiedź. Ale jako zwiastun mogę ci dać ostatnią linia tej odpowiedzi: Twój klient powie ci, ile elastyczności i ile warstw uogólnienia powinieneś szukać.
Wiele z tych odpowiedzi zawiera konkretne porady. Chciałem dać coś bardziej ogólnego ... ponieważ ironia jest zbyt zabawna, aby z niej zrezygnować!
Jak zauważyły niektóre odpowiedzi, ogólność jest droga. Jednak tak naprawdę nie jest. Nie zawsze. Zrozumienie kosztów ma zasadnicze znaczenie dla grania w gry wielokrotnego użytku.
Skupiam się na przestawianiu skali na „nieodwracalne” na „odwracalne”. To płynna skala. Jedyną naprawdę nieodwracalną rzeczą jest „czas spędzony na projekcie”. Nigdy nie odzyskasz tych zasobów. Nieco odwracalne mogą być sytuacje „złotych kajdanek”, takie jak Windows API. Przestarzałe funkcje pozostają w tym interfejsie API przez dziesięciolecia, ponieważ wymaga tego model biznesowy Microsoft. Jeśli masz klientów, których relacje zostałyby trwale uszkodzone przez cofnięcie niektórych funkcji API, należy to traktować jako nieodwracalne. Patrząc na drugi koniec skali, masz na przykład prototypowy kod. Jeśli nie podoba ci się, dokąd idzie, możesz po prostu go wyrzucić. Nieco odwracalne mogą być interfejsy API do użytku wewnętrznego. Można je refaktoryzować bez przeszkadzania klientowi,czas (najbardziej nieodwracalny zasób ze wszystkich!)
Więc umieść je na skali. Teraz możesz zastosować heurystykę: im bardziej coś jest odwracalne, tym więcej możesz użyć do przyszłych działań. Jeśli coś jest nieodwracalne, używaj go tylko do konkretnych zadań kierowanych przez klienta. Dlatego widzisz zasady podobne do tych z ekstremalnego programowania, które sugerują robienie tylko tego, o co prosi klient, i nic więcej. Zasady te są dobre w upewnieniu się, że nie robisz czegoś, czego żałujesz.
Rzeczy takie jak zasada DRY sugerują sposób na zmianę tej równowagi. Jeśli się powtarzasz, jest to okazja do stworzenia zasadniczo wewnętrznego interfejsu API. Żaden klient tego nie widzi, więc zawsze możesz to zmienić. Gdy już będziesz mieć ten wewnętrzny interfejs API, możesz zacząć grać z przyszłościowymi rzeczami. Jak myślisz, ile różnych zadań związanych ze strefą czasową powierzy ci żona? Czy masz innych klientów, którzy mogą chcieć zadań opartych na strefie czasowej? Twoja elastyczność tutaj jest kupowana przez konkretne wymagania twoich obecnych klientów i wspiera potencjalne przyszłe wymagania przyszłych klientów.
To podejście do myślenia warstwowego, które naturalnie pochodzi z DRY, zapewnia generalizację, której potrzebujesz bez marnotrawstwa. Ale czy jest jakiś limit? Oczywiście że tak. Ale żeby to zobaczyć, musisz zobaczyć las dla drzew.
Jeśli masz wiele warstw elastyczności, często prowadzą one do braku bezpośredniej kontroli warstw napotykanych przez klientów. Miałem oprogramowanie, w którym miałem brutalne zadanie wyjaśnienia klientowi, dlaczego nie może mieć tego, czego chce ze względu na elastyczność wbudowaną w 10 warstw, których nigdy nie powinni widzieć. Napisaliśmy się w kąt. Zawiązaliśmy się w węzeł z całą elastycznością, która naszym zdaniem była potrzebna.
Więc kiedy wykonujesz tę sztuczkę uogólnienia / OSUSZANIA, zawsze miej puls na swoim kliencie . Jak myślisz, o co poprosi twoja żona? Czy jesteś w stanie zaspokoić te potrzeby? Jeśli masz talent, klient skutecznie powie ci o swoich przyszłych potrzebach. Jeśli nie masz talentu, cóż, większość z nas polega na zgadywaniu! (szczególnie w przypadku małżonków!) Niektórzy klienci będą chcieli dużej elastyczności i chętnie zaakceptują dodatkowe koszty związane z rozwojem wszystkich tych warstw, ponieważ bezpośrednio korzystają z elastyczności tych warstw. Inni klienci mają raczej stałe wymagania dotyczące niezachwiania i wolą bardziej bezpośredni rozwój. Twój klient powie ci, ile elastyczności i ile warstw uogólnienia powinieneś szukać.
źródło
Your customer will tell you how much flexibility and how many layers of generalization you should seek.
to za świat?Jest to coś znanego w kręgach zaawansowanych inżynierów oprogramowania jako „żart”. Żarty nie muszą być tym, co nazywamy „prawdziwym”, chociaż aby być zabawnym, zazwyczaj muszą wskazywać na coś prawdziwego.
W tym konkretnym przypadku „żart” nie jest „prawdziwy”. Praca polegająca na napisaniu ogólnej procedury niszczenia dowolnego miasta jest, możemy spokojnie założyć, o rząd wielkości przekraczający wymagany do zniszczenia jednego konkretnego miastaMiasto. W przeciwnym razie każdy, kto zniszczył jedno lub kilka miast (biblijny Joshua, powiedzmy, lub Prezydent Truman), mógłby w trywialny sposób uogólnić to, co zrobił i być w stanie zniszczyć absolutnie dowolne miasto do woli. W rzeczywistości tak nie jest. Metody, których ci dwaj ludzie znani używali do zniszczenia niewielkiej liczby konkretnych miast, niekoniecznie działałyby w każdym mieście w dowolnym momencie. Inne miasto, którego mury miały inną częstotliwość rezonansową lub których obrony powietrzne na dużych wysokościach były raczej lepsze, wymagałoby niewielkich lub fundamentalnych zmian podejścia (inaczej trąbka lub rakieta o innej wysokości).
Prowadzi to również do utrzymania kodu wbrew zmianom z upływem czasu: obecnie jest raczej wiele miast, które nie byłyby objęte żadnym z tych podejść, dzięki nowoczesnym metodom budowy i wszechobecnemu radarowi.
Opracowanie i przetestowanie całkowicie ogólnych środków, które zniszczą dowolne miasto, zanim zgodzisz się zniszczyć tylko jedno miasto, jest desperacko nieefektywnym podejściem. Żaden etycznie wyszkolony inżynier oprogramowania nie próbowałby uogólnić problemu w stopniu wymagającym zamówień o wielkości większej niż praca, za którą ich pracodawca / klient musiałby zapłacić, bez wykazanego wymogu.
Co jest prawdą? Czasami dodanie ogólności jest banalne. Czy powinniśmy zatem zawsze dodawać ogólności, gdy jest to trywialne? Nadal będę argumentować „nie, nie zawsze”, ze względu na problem z utrzymaniem długoterminowym. Przypuśćmy, że w momencie pisania tego tekstu wszystkie miasta są w zasadzie takie same, więc wybieram DestroyCity. Kiedyś to napisałem, wraz z testami integracji, które (ze względu na skończoną liczbę pól danych wejściowych) iterują po każdym znanym mieście i upewniają się, że funkcja działa na każdym (nie wiem, jak to działa. Prawdopodobnie wywołuje City.clone () i niszczy klon? W każdym razie).
W praktyce funkcja ta służy wyłącznie do zniszczenia Bagdadu, załóżmy, że ktoś buduje nowe miasto odporne na moje techniki (jest głęboko pod ziemią lub coś w tym rodzaju). Teraz mam błąd testu integracji dla przypadku użycia, który tak naprawdę nie istnieje , i zanim będę mógł kontynuować kampanię terroru przeciwko niewinnym cywilom Iraku, muszę wymyślić, jak zniszczyć Subterrania. Nieważne, czy jest to etyczne, czy nie, jest głupie i marnuje mój cholerny czas .
Czy naprawdę potrzebujesz funkcji, która może generować oszczędności światła dziennego na dowolny rok, tylko do danych na 2018 r.? Być może, ale z pewnością będzie to wymagało niewielkiego dodatkowego wysiłku po prostu zebranie przypadków testowych. Uzyskanie lepszej bazy danych stref czasowych może wymagać dużego wysiłku niż ta, którą faktycznie masz. Na przykład w 1908 r. W mieście Port Arthur w Ontario okres DST rozpoczął się 1 lipca. Czy to jest w bazie danych strefy czasowej twojego systemu operacyjnego? Nie myślałem, więc twoja uogólniona funkcja jest błędna . Nie ma nic szczególnie etycznego w pisaniu kodu, który składa obietnice, których nie może dotrzymać.
W porządku, więc z odpowiednimi zastrzeżeniami łatwo jest napisać funkcję, która wykonuje strefy czasowe przez szereg lat, powiedzmy od 1970 roku. Ale równie łatwo jest wziąć faktycznie napisaną funkcję i uogólnić ją, aby sparametryzować rok. Uogólnienie nie jest więc tak naprawdę bardziej etyczne / rozsądne, aby zrobić to, co zrobiłeś, a następnie uogólnić, jeśli i kiedy tego potrzebujesz.
Jeśli jednak wiesz, dlaczego twoja żona chciała sprawdzić tę listę DST, miałbyś świadomą opinię, czy ona może ponownie zadać to samo pytanie w 2019 r., A jeśli tak, to czy możesz wyjść z tej pętli, dając jej funkcję, którą może wywołać, bez konieczności jej ponownej kompilacji. Po wykonaniu tej analizy odpowiedź na pytanie „czy uogólnić to na ostatnie lata” może brzmieć „tak”. Ale stwarzasz sobie kolejny problem, że dane w strefie czasowej w przyszłości są tylko tymczasowe i dlatego jeśli uruchomi je dzisiaj na 2019 r., może, ale nie musi, zdawać sobie sprawę z tego, że podsyca ją. Więc nadal musisz napisać wiele dokumentacji, która nie byłaby potrzebna do mniej ogólnej funkcji („dane pochodzą z bazy danych stref czasowych bla bla bla bla link, aby zobaczyć ich zasady dotyczące przekazywania aktualizacji bla bla bla”). Jeśli odrzucisz ten szczególny przypadek, to przez cały czas to robisz, ona nie może zająć się zadaniem, dla którego potrzebowała danych z 2018 r., Z powodu bzdur o 2019 r., O których nawet nie dba.
Nie rób trudnych rzeczy bez odpowiedniego ich przemyślenia, tylko dlatego, że żart kazał ci to zrobić. To jest użyteczne? Czy jest wystarczająco tani jak na ten stopień przydatności?
źródło
Jest to łatwa do narysowania linia, ponieważ wielokrotność użytkowania zdefiniowana przez architektów-astronautów to bzdury.
Prawie cały kod stworzony przez twórców aplikacji jest wyjątkowo specyficzny dla domeny. To nie jest rok 1980. Prawie wszystko, co warto zawracać sobie głowę, jest już w ramach.
Abstrakcje i konwencje wymagają dokumentacji i wysiłku uczenia się. Uprzejmie przestańcie tworzyć nowe tylko ze względu na to. (Patrzę na ciebie , ludzie JavaScript!)
Pozwólmy sobie na nieprawdopodobną fantazję, że znalazłeś coś, co naprawdę powinno znaleźć się w twoim wyborze. Nie możesz tak po prostu podnieść kodu w zwykły sposób. O nie, potrzebujesz pokrycia testowego nie tylko do zamierzonego zastosowania, ale także do odstępstw od zamierzonego zastosowania, dla wszystkich znanych przypadków skrajnych, dla wszystkich możliwych trybów awarii, przypadków testowych do diagnostyki, danych testowych, dokumentacji technicznej, dokumentacji użytkownika, zarządzania wydaniami, wsparcia skrypty, testy regresji, zarządzanie zmianami ...
Czy Twój pracodawca chętnie za to wszystko płaci? Powiem nie.
Abstrakcja to cena, którą płacimy za elastyczność. To sprawia, że nasz kod jest bardziej złożony i trudniejszy do zrozumienia. O ile elastyczność nie zaspokoi rzeczywistej i obecnej potrzeby, po prostu nie, ponieważ YAGNI.
Spójrzmy na przykład z prawdziwego świata, z którym właśnie miałem do czynienia: HTMLRenderer. To nie skalowało się poprawnie, gdy próbowałem renderować w kontekście urządzenia drukarki. Cały dzień zajęło mi odkrycie, że domyślnie używa GDI (który nie skaluje się), a nie GDI + (który robi, ale nie antialias), ponieważ musiałem przedzierać się przez sześć poziomów pośrednich w dwóch złożeniach, zanim znalazłem kod, który zrobił cokolwiek .
W takim przypadku wybaczę autorowi. Abstrakcja jest faktycznie konieczna, ponieważ jest to kod frameworka, który jest ukierunkowany na pięć bardzo różnych celów renderowania: WinForms, WPF, dotnet Core, Mono i PdfSharp.
Ale to tylko podkreśla mój punkt widzenia: prawie na pewno nie robisz czegoś niezwykle złożonego (renderowanie HTML z arkuszami stylów) na wielu platformach, z określonym celem wysokiej wydajności na wszystkich platformach.
Twój kod jest prawie na pewno kolejną siatką baz danych z regułami biznesowymi, które dotyczą tylko twojego pracodawcy, oraz przepisami podatkowymi, które obowiązują tylko w twoim stanie, w aplikacji, która nie jest na sprzedaż.
Cała ta pośrednia rozwiązuje problem, którego nie masz, i sprawia, że twój kod jest znacznie trudniejszy do odczytania, co ogromnie zwiększa koszty utrzymania i jest ogromną szkodą dla twojego pracodawcy. Na szczęście ludzie, którzy powinni na to narzekać, nie są w stanie zrozumieć, co im robisz.
Kontrargumentem jest to, że ten rodzaj abstrakcji wspiera rozwój oparty na testach, ale myślę, że TDD to także bzdury, ponieważ zakłada, że firma ma jasne, kompletne i prawidłowe zrozumienie swoich wymagań. TDD jest świetny dla NASA i oprogramowania sterującego sprzętem medycznym i samojezdnymi samochodami, ale o wiele za drogi dla wszystkich innych.
Nawiasem mówiąc, nie można przewidzieć wszystkich oszczędności światła dziennego na świecie. W szczególności Izrael ma około 40 przejść każdego roku, które przeskakują wszędzie, ponieważ nie możemy mieć ludzi modlących się w niewłaściwym czasie, a Bóg nie oszczędza na świetle dziennym.
źródło
Jeśli używasz co najmniej java 8, napiszesz klasę WorldTimeZones, aby dostarczyć coś, co w istocie wydaje się być zbiorem stref czasowych.
Następnie dodaj metodę filter (Predicate filter) do klasy WorldTimeZones. Daje to dzwoniącemu możliwość filtrowania wszystkiego, co chce, poprzez przekazanie wyrażenia lambda jako parametru.
Zasadniczo metoda pojedynczego filtra obsługuje filtrowanie wszystkiego zawartego w wartości przekazywanej do predykatu.
Alternatywnie dodaj metodę stream () do swojej klasy WorldTimeZones, która po wywołaniu tworzy strumień stref czasowych. Następnie dzwoniący może filtrować, mapować i zmniejszać według potrzeb bez pisania żadnych specjalizacji.
źródło