Czym tak naprawdę jest „miękkie kodowanie”?

87

W tym artykule Alexa Papadimoulisa możesz zobaczyć ten fragment:

private void attachSupplementalDocuments()
{
  if (stateCode == "AZ" || stateCode == "TX") {

    //SR008-04X/I are always required in these states
    attachDocument("SR008-04X");
    attachDocument("SR008-04XI");
  }

  if (ledgerAmnt >= 500000) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
  }

  if (coInsuredCount >= 5  && orgStatusCode != "CORP") {
    //Non-CORP orgs with 5 or more co-ins require AUTHCNS-1A
    attachDocument("AUTHCNS-1A");
  }
}

Naprawdę nie rozumiem tego artykułu.

Cytuję:

Gdyby każda stała reguły biznesowej była przechowywana w jakimś pliku konfiguracyjnym, życie byłoby znacznie [bardziej ( sic )] trudne dla wszystkich, którzy utrzymują oprogramowanie: byłoby wiele plików kodu, które dzieliłyby jeden duży plik (lub odwrotnie, wiele małych plików konfiguracyjnych); wdrażanie zmian w regułach biznesowych nie wymaga nowego kodu, ale ręczną zmianę plików konfiguracyjnych; a debugowanie jest o wiele trudniejsze.

Jest to argument przeciwko posiadaniu stałej liczby całkowitej „500000” w pliku konfiguracyjnym lub „AUTHCNS-1A” i innych stałych ciągów.

Jak może to być złą praktyką?

W tym fragmencie „500000” nie jest liczbą. Nie jest to na przykład to samo, co:

int doubleMe(int a) { return a * 2;}

gdzie 2 to liczba, która nie musi być abstrakcyjna. Jego użycie jest oczywiste i nie reprezentuje czegoś, co można ponownie wykorzystać później.

Przeciwnie, „500000” to nie tylko liczba. Jest to znacząca wartość, która reprezentuje ideę punktu przerwania w funkcjonalności. Ten numer może być użyty w więcej niż jednym miejscu, ale nie jest to numer, którego używasz; jest to idea granicy / granicy, poniżej której obowiązuje jedna reguła, a powyżej której inna.

W jaki sposób odwoływanie się do niego z pliku konfiguracyjnego, a nawet dowolnego #define, constlub innego języka dostarczanego przez język, jest gorsze niż uwzględnienie jego wartości? Jeśli później program lub jakiś inny programista również wymaga tej granicy, aby oprogramowanie dokonało innego wyboru, jesteś wkręcony (ponieważ gdy się zmienia, nic nie gwarantuje, że zmieni się w obu plikach). Jest to wyraźnie gorsze w przypadku debugowania.

Ponadto, jeśli jutro rząd zażąda „Od 5/3/2050 musisz dodać AUTHLDG-122B zamiast AUTHLDG-1A”, ta stała ciągu nie jest zwykłą stałą ciągu. Jest to jeden pomysł; jest to tylko bieżąca wartość tego pomysłu (czyli „rzecz, którą dodajesz, jeśli księga jest większa niż 500k”).

Pozwól mi wyjaśnić. Nie twierdzę, że artykuł jest zły; Po prostu tego nie rozumiem; może nie jest to zbyt dobrze wyjaśnione (przynajmniej dla mojego myślenia).

Rozumiem, że zastąpienie każdej możliwej wartości literału lub wartości liczbowej stałą, definicją lub zmienną konfiguracyjną nie tylko nie jest konieczne, ale też komplikuje rzeczy, ale ten konkretny przykład nie wydaje się należeć do tej kategorii. Skąd wiesz, że nie będziesz go później potrzebować? A może ktoś inny w tej sprawie?

K. Gkinis
źródło
21
Zagraj w puzzle: jakie byłoby dobre imię dla tych liczb? Myślę, że przekonasz się, że albo nazwa nie wnosi żadnej wartości, albo opisuje wszystko, co kod już opisuje i często dodaje niejasności („LedgerLimitForAuthDlg1A”?). Uważam, że artykuł jest genialny właśnie ze względu na to, jak istotny jest to temat. Utrzymałem systemy, które korzystały z obu podejść i mogę powiedzieć, że reguły biznesowe należą do kodu - znacznie ułatwia ich śledzenie, utrzymanie i zrozumienie. Korzystając z konfiguracji, lepiej ją policz - jest znacznie droższa.
Luaan,
2
Konfiguracja powinna być zarezerwowana na rzeczy, które należy skonfigurować. Jeśli reguły biznesowe nie są ogólnie konfigurowalne, i tak ich skonfigurowanie nic nie kosztuje.
biziclop,
W przypadku odpowiednio zaawansowanych języków konfiguracja przyjmuje postać rzeczywistych podprogramów, a nie ciągów znaków.
Thorbjørn Ravn Andersen

Odpowiedzi:

100

Autor ostrzega przed przedwczesną abstrakcją.

Linia if (ledgerAmt > 500000)wygląda jak reguła biznesowa, której można oczekiwać w przypadku dużych, złożonych systemów biznesowych, których wymagania są niezwykle złożone, ale precyzyjne i dobrze udokumentowane.

Zazwyczaj tego rodzaju wymagania są przypadkami wyjątkowymi / skrajnymi, a nie użyteczną logiką wielokrotnego użytku. Te wymagania są zazwyczaj własnością analityków biznesowych i ekspertów merytorycznych, a nie inżynierów

(Należy pamiętać, że „własność” wymagań analityków biznesowych / ekspertów w takich przypadkach zwykle występuje, gdy programiści pracujący w specjalistycznych dziedzinach nie mają wystarczającej wiedzy specjalistycznej w dziedzinie; chociaż nadal oczekiwałbym pełnej komunikacji / współpracy między programistami a ekspertami w dziedzinie ochrony przed niejednoznaczne lub źle napisane wymagania).

Utrzymując systemy, których wymagania są pełne przypadków skrajnych i bardzo skomplikowanej logiki, zwykle nie ma sposobu na użyteczne streszczenie tej logiki lub uczynienie jej bardziej konserwowalną; próby zbudowania abstrakcji mogą z łatwością przynieść efekt odwrotny - nie tylko skutkują marnowaniem czasu, ale także skutkują mniejszym kodem.

W jaki sposób odwołuje się do niego z pliku konfiguracyjnego, a nawet #define, const lub cokolwiek, co zapewnia Twój język, gorzej niż uwzględnienie jego wartości? Jeśli później program lub jakiś inny programista również wymaga tej granicy, aby oprogramowanie dokonało innego wyboru, jesteś wkręcony (ponieważ gdy się zmienia, nic nie gwarantuje, że zmieni się w obu plikach). Jest to wyraźnie gorsze w przypadku debugowania.

Tego rodzaju kod jest zwykle chroniony przez fakt, że sam kod prawdopodobnie ma indywidualne mapowanie do wymagań; tzn. gdy programista wie, że 500000liczba pojawia się dwukrotnie w wymaganiach, ten programista również wie, że pojawia się dwukrotnie w kodzie.

Rozważ inny (równie prawdopodobny) scenariusz, w którym 500000pojawia się w wielu miejscach w dokumencie wymagań, ale eksperci w sprawach przedmiotowych decydują się na zmianę tylko jednego z nich; istnieje jeszcze gorsze ryzyko, że ktoś zmieniający constwartość może nie zdawać sobie sprawy, że 500000jest używany do oznaczania różnych rzeczy - więc programista zmienia to w jednym i jedynym miejscu, w którym znajduje ją w kodzie, i kończy się uszkodzeniem czegoś, co nie zdawałem sobie sprawy, że się zmienili.

Ten scenariusz zdarza się często w niestandardowym oprogramowaniu prawnym / finansowym (np. Logika wyceny ubezpieczenia) - ludzie, którzy piszą takie dokumenty, nie są inżynierami i nie mają problemu z kopiowaniem + wklejaniem całych fragmentów specyfikacji, modyfikowaniem kilku słów / liczb, ale pozostawiając większość tego samego.

W tych scenariuszach najlepszym sposobem radzenia sobie z wymaganiami kopiowania i wklejania jest pisanie kodu kopiuj-wklej i sprawienie, aby kod wyglądał jak najbardziej zbliżony do wymagań (w tym kodowanie wszystkich danych), jak to możliwe.

Rzeczywistość takich wymagań polega na tym, że zwykle nie pozostają długo kopiowane i wklejane, a wartości czasami zmieniają się regularnie, ale często nie zmieniają się w tandemie, więc starają się zracjonalizować lub wyodrębnić te wymagania lub uprościć w jakikolwiek sposób powoduje więcej problemów z konserwacją niż zwykłe tłumaczenie wymagań dosłownie na kod.

Ben Cottrell
źródło
28
Język specyficzny dla domeny (DSL) może być dobrym sposobem na uczynienie kodu bardziej podobnym do dokumentu wymagań.
Ian
13
Kolejną zaletą DSL jest to, że utrudnia to przypadkowe połączenie logiki aplikacji, prezentacji lub trwałości z regułami biznesowymi.
Erik Eidt,
16
Myślenie, że twoja aplikacja jest na tyle wyjątkowa, by gwarantować jej własną DSL, jest zwykle pychą.
brian_o
8
Those requirements are typically owned and maintained by business analysts and subject matter experts, rather than by engineersco nie zawsze jest dobrym pomysłem. Czasami akt przekształcenia tych wymagań w kod ujawni przypadki narożne, w których wymagania albo nie są dobrze zdefiniowane, albo są zdefiniowane w sposób sprzeczny z interesem biznesowym. Jeśli analitycy biznesowi i programiści mogą współpracować w osiąganiu wspólnego celu, można uniknąć wielu problemów.
kasperd
4
@BenCottrell Nie sugerowałem zmiany zasad, aby ułatwić pisanie oprogramowania. Ale kiedy masz wiele warunków warunkowych w regułach, jest całkiem możliwe, że podczas interakcji zdefiniowano reguły. Ale gdy zamienisz specyfikację w kod, programista z pewnością zauważy, że istnieje możliwość interakcji między tymi warunkami. W tym momencie deweloper może stwierdzić, że ścisła interpretacja specyfikacji prowadzi do niezamierzonej ceny, która pozwoliłaby klientom na grę w system.
kasperd
44

Artykuł ma dobrą rację. Jak może być złą praktyką wyodrębnianie stałych do pliku konfiguracyjnego? Może to być złą praktyką, jeśli niepotrzebnie komplikuje kod. Posiadanie wartości bezpośrednio w kodzie jest znacznie prostsze niż czytanie jej z pliku konfiguracyjnego, a kod w postaci, w której napisano, jest łatwy do naśladowania.

Ponadto jutro rząd mówi „Od 5/3/2050 musisz dodać AUTHLDG-122B zamiast AUTHLDG-1A”.

Tak, a następnie zmieniasz kod. Chodzi o to, że zmiana kodu nie jest bardziej skomplikowana niż zmiana pliku konfiguracyjnego.

Podejście opisane w tym artykule nie jest skalowane, jeśli otrzymujesz bardziej złożoną logikę, ale chodzi o to, że musisz dokonać oceny, a czasem najprostsze rozwiązanie jest po prostu najlepsze.

Skąd wiesz, że nie będziesz go później potrzebować? A może ktoś inny w tej sprawie?

Na tym polega zasada YAGNI. Nie projektuj na nieznaną przyszłość, która może okazać się zupełnie inna, projekt na teraźniejszość. Masz rację, że jeśli wartość 500000 zostanie użyta w kilku miejscach w programie, to oczywiście należy ją wyodrębnić do stałej. Ale nie jest tak w przypadku omawianego kodu.

Softcoding jest tak naprawdę kwestią oddzielenia problemów . Informacje softcode, które znasz, mogą ulec zmianie niezależnie od logiki podstawowej aplikacji. Nigdy nie kodowałbyś na stałe parametrów połączenia z bazą danych, ponieważ wiesz, że może on ulec zmianie niezależnie od logiki aplikacji i będziesz musiał rozróżnić go dla różnych środowisk. W aplikacji internetowej lubimy oddzielać logikę biznesową od szablonów HTML i arkuszy stylów, ponieważ mogą one zmieniać się niezależnie, a nawet mogą być zmieniane przez różne osoby.

Jednak w przypadku kodu przykładowe ciągi i liczby zakodowane na stałe stanowią integralną część logiki aplikacji. Można sobie wyobrazić, że jeden plik może zmienić swoją nazwę z powodu pewnych zmian zasad poza twoją kontrolą, ale równie możliwe jest, że musimy dodać nowy sprawdzanie gałęzi if dla innego warunku. Wyodrębnienie nazw plików i liczb faktycznie przerywa spójność w tym przypadku.

JacquesB
źródło
4
Często zmiana kodu jest dużo bardziej skomplikowana niż plik konfiguracyjny. Może być potrzebny programista i cykl kompilacji systemu / wydania dla tego pierwszego, podczas gdy ten drugi wymaga jedynie zmiany numeru w polu w przyjaznym interfejsie konfiguracji.
OrangeDog
6
@OrangeDog Tak, na początku tak to wygląda. Ale jeśli robisz takie rzeczy, UI config będzie niczego , ale przyjazny, z setkami bezsensowne tekstowych skrzynek z prośbą o kto wie co. A teraz musisz zbudować interfejs użytkownika i udokumentować go. Pamiętaj, że to nie znaczy, że konfiguracja nigdy nie jest dobrym rozwiązaniem - istnieją przypadki, w których jest to absolutnie właściwy wybór. Ale nie w żadnym z przykładów w artykule. A kiedy ostatni raz ustawodawstwo zmieniło tylko liczbę? Ostatnim razem, gdy zmieniły się tutaj zasady VAT, musieliśmy powtórzyć wszystkie obliczenia.
Luaan,
2
@OrangeDog: Zakładasz tutaj, że konfiguracja oprogramowania zapewnia niezbędne zaczepy do kontroli, którą musisz wykonać. Zauważ, że w OP każdy z nich ifoparty jest na innej zmiennej! Jeśli potrzebna zmienna nie jest dostępna w konfiguracji, musisz zmodyfikować oprogramowanie.
Matthieu M.
2
@OrangeDog, więc sugerujesz, że powinny nastąpić znaczące zmiany w logice aplikacji, bez cyklu deweloperskiego / qa / wydania i odpowiedniego testowania?
NPSF3000,
3
@OrangeDog: OK używasz YAML do konfigurowania logiki w przykładzie. Ponieważ logika zawiera reguły warunkowe, w YAML można znaleźć sposób na przedstawienie tych warunków. Gratulacje, odkryłeś na nowo Python. Dlaczego więc nie napisać całej aplikacji w Pythonie?
JacquesB
26

Artykuł mówi dalej o „Enterprise Rule Engine”, które są prawdopodobnie lepszym przykładem tego, z czym się kłóci.

Logika polega na tym, że można uogólniać do momentu, w którym konfiguracja staje się tak skomplikowana, że ​​zawiera własny język programowania.

Na przykład kod stanu do mapowania dokumentu w przykładzie można przenieść do pliku konfiguracyjnego. Ale wtedy musisz wyrazić złożony związek.

<statecode id="AZ">
    <document id="SR008-04X"/>
    <document id="SR008-04XI"/>
</statecode>

Może również umieściłbyś kwotę w księdze?

<statecode id="ALL">
    <document id="AUTHLDG-1A" rule="ledgerAmt >= 50000"/>
</statecode>

Wkrótce okazuje się, że programujesz w nowym, wymyślonym przez siebie języku i zapisujesz ten kod w plikach konfiguracyjnych, które nie mają kontroli źródła ani zmiany.

Należy zauważyć, że ten artykuł pochodzi z 2007 r., Kiedy tego rodzaju podejście było powszechne.

W dzisiejszych czasach prawdopodobnie rozwiązalibyśmy problem za pomocą wstrzykiwania zależności (DI). Tj. Miałbyś „zakodowane na stałe”

InvoiceRules_America2007 : InvoiceRules

który zastąpiłbyś na stałe lub bardziej konfigurowalny

InvoiceRules_America2008 : InvoiceRules

kiedy zmieniają się wymagania prawne lub biznesowe.

Ewan
źródło
4
Być może powinieneś zdefiniować „DI”. I może wyjaśnić trochę więcej.
Basil Bourque,
9
Dlaczego ten plik nie miałby być w systemie kontroli źródła?
JDługosz
2
Jeśli jest to specyficzne dla klienta, to czy w zakodowanej wersji występuje ogromny bałagan ifinstrukcji, które dają różne wartości dla każdego klienta? To brzmi jak coś, co powinno znajdować się w pliku konfiguracyjnym. Bycie w jednym lub innym rodzaju pliku, przy czym wszystkie pozostałe są równe, nie jest powodem, aby nie kontrolować / śledzić / tworzyć kopii zapasowej pliku. @ewan wydaje się mówić, że z jakiegoś powodu nie można zapisać pliku DSL w ramach projektu, nawet jeśli zasoby inne niż kodowe, takie jak obrazy, pliki dźwiękowe i dokumentacja, z pewnością .
JDługosz
2
Naprawdę powinieneś refaktoryzować wartość „50000” z kodu XML i umieścić ją w osobnym pliku konfiguracyjnym, nie sądzisz? ... a tak przy okazji, ma to być 500000.
Wildcard,
1
@jdlugosz koncepcja ERE polega na tym, że kupujesz system, a następnie konfigurujesz go do swoich potrzeb. być może dlatego, że wewnętrzni twórcy konkurowali z tymi „elastycznymi” systemami, spróbowaliby je naśladować. zmiana kontroli konfiguracji, nawet w systemach dużych firm, takich jak IBM, była często późniejsza refleksja. Punktem sprzedaży była szybka zmiana
Ewan
17

Przeciwnie, „500000” to nie tylko liczba. Jest to znacząca wartość, która reprezentuje ideę punktu przerwania w funkcjonalności. Ta liczba może być używana w więcej niż jednym miejscu, ale nie jest to liczba, której używasz, to idea limitu / granicy, poniżej której obowiązuje jedna reguła, a powyżej której inna.

Wyrazem tego jest (i mogę argumentować, że nawet komentarz jest zbędny):

 if (ledgerAmnt >= 500000) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
  }

To tylko powtarzanie tego, co robi kod:

LEDGER_AMOUNT_REQUIRING_AUTHLDG1A=500000
if (ledgerAmnt >= LEDGER_AMOUNT_REQUIRING_AUTHLDG1A) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
}

Zauważ, że autor zakłada, że ​​znaczenie 500000 jest powiązane z tą regułą; nie jest to wartość, która jest lub może być ponownie wykorzystana w innym miejscu:

Jedyną zmianą reguły biznesowej, którą to wcześniejsze kodowanie miękkie mogło kiedykolwiek uwzględnić, jest zmiana kwoty księgi, która wymagała formularza AUTHLDG-1A. Wszelkie inne zmiany reguł biznesowych wymagałyby jeszcze więcej pracy - konfiguracji, dokumentacji, kodu itp

Moim zdaniem główny punkt tego artykułu polega na tym, że czasami liczba jest po prostu liczbą: nie ma ona żadnego dodatkowego znaczenia poza tym, co jest zawarte w kodzie i nie jest prawdopodobne, aby była używana gdzie indziej. Dlatego niezręczne podsumowanie tego, co robi kod (teraz) w nazwie zmiennej tylko w celu uniknięcia zakodowanych wartości, jest w najlepszym razie niepotrzebnym powtórzeniem.

Thanos Tintinidis
źródło
2
Jeśli wprowadzisz stałą LEDGER_AMOUNT_REQUIRING_AUTHLDG1A, nie będziesz już wpisywać komentarza do kodu. Komentarze nie są dobrze utrzymywane przez programistów. Jeśli kwota kiedykolwiek się zmieni, ifwarunek i komentarz przestaną być zsynchronizowane. Przeciwnie, stała LEDGER_AMOUNT_REQUIRING_AUTHLDG1Anigdy nie zsynchronizuje się z samym sobą i wyjaśnia swój cel bez niepotrzebnego komentarza.
ZeroOne
2
@ZeroOne: Z wyjątkiem tego, że jeśli reguła biznesowa zmieni się na „Księgę 500 KB lub więcej wymaga AUTHLDG-1A i AUTHLDG-2B”, jest bardzo prawdopodobne, że osoba, która doda attachDocument("AUTHLDG-2B");linię, nie zaktualizuje stałej nazwy w tym samym czasie. W tym przypadku myślę, że kod jest dość jasny, bez komentarza ani zmiennej wyjaśniającej. (Chociaż może mieć sens konwencja wskazywania odpowiedniej sekcji dokumentu wymagań biznesowych za pomocą komentarzy do kodu. Zgodnie z tą konwencją komentarz do kodu, który to robi , byłby odpowiedni tutaj.)
ruakh
@ruakh, OK, a następnie zmienię stałą do wywołania LEDGER_AMOUNT_REQUIRING_ADDITIONAL_DOCUMENTS(co prawdopodobnie powinienem był zrobić w pierwszej kolejności). Mam też zwyczaj umieszczania identyfikatorów wymagań biznesowych w komunikatach zatwierdzania Git, a nie w kodzie źródłowym.
ZeroOne
1
@ZeroOne: Ale dla AUTHLDG-3C kwota księgi jest w rzeczywistości maksymalna . W przypadku AUTHLDG-4D odpowiednia kwota księgi zależy od stanu. (Czy masz już rację? W przypadku tego typu kodu chcesz, aby kod odzwierciedlał reguły biznesowe, a nie próbę wyodrębnienia reguł biznesowych, ponieważ nie ma powodu oczekiwać, że ewolucja reguł biznesowych będzie zgodna z abstrakcje, które adoptowałeś.)
ruakh 10.04.16
2
Osobiście nie sprzeciwiam się umieszczeniu magicznej liczby w kodzie, sprzeciwiam się strukturze kodu, więc potrzebuje tych komentarzy. Gdybym to był ja, uczyniłbym każdy dokument instancją enum własną attachIfNecessary()metodą i po prostu zapętliłem wszystkie.
David Moles,
8

Pozostałe odpowiedzi są poprawne i przemyślane. Ale oto moja krótka i słodka odpowiedź.

  Rule/value          |      At Runtime, rule/value…
  appears in code:    |   …Is fixed          …Changes
----------------------|------------------------------------
                      |                 |
  Once                |   Hard-code     |   Externalize
                      |                 |   (soft-code)
                      |                 |
                      |------------------------------------
                      |                 |
  More than once      |   Soft-code     |   Externalize
                      |   (internal)    |   (soft-code)
                      |                 |
                      |------------------------------------

Jeśli reguły i wartości specjalne pojawiają się w jednym miejscu w kodzie i nie zmieniają się w czasie wykonywania, należy wpisać kod stały, jak pokazano w pytaniu.

Jeśli reguły lub wartości specjalne pojawiają się w więcej niż jednym miejscu w kodzie i nie zmieniają się w czasie wykonywania, wówczas należy użyć kodu programowego. Miękkie kodowanie reguły może oznaczać zdefiniowanie konkretnej klasy / metody lub użycie wzorca Builder . W przypadku wartości miękkie kodowanie może oznaczać zdefiniowanie pojedynczej stałej lub wyliczenia dla wartości, która będzie używana w całym kodzie.

Jeśli reguły lub wartości specjalne mogą ulec zmianie w czasie wykonywania, należy je eksternalizować. Zwykle odbywa się to poprzez aktualizację wartości w bazie danych. Lub zaktualizuj wartości w pamięci ręcznie przez użytkownika wprowadzającego dane. Odbywa się to również poprzez przechowywanie wartości w pliku tekstowym (XML, JSON, zwykły tekst, cokolwiek), który jest wielokrotnie skanowany w celu zmiany daty i godziny modyfikacji pliku.

Basil Bourque
źródło
1
Podoba mi się twoja odpowiedź, ale myślę, że powinieneś również rozważyć, czy zmieni się ona w trakcie wdrażania. Jest to szczególnie istotne, jeśli rzecz jest produktem, który będzie używany w wielu organizacjach, które mogą na przykład mieć inne zasady dotyczące tego, czy osoba nadzorująca musi zatwierdzić zwrot kosztów w stosunku do X itp.
Bloke Down The Pub
Zgodził się zarówno z tą odpowiedzią, jak i komentarzem na temat wdrożenia. Rzeczy, nad którymi pracuję, są wdrażane przez wiele organizacji i wiele z nich wymaga subtelnie różnych wartości. Zazwyczaj przechowujemy te „ustawienia” w bazie danych, a nie w pliku konfiguracyjnym, ale zasada jest taka, że ​​nie chcemy tworzyć różnych wersji naszego oprogramowania dla każdej firmy, która je wdraża (następnie powtarzaj te różne wersje przy każdej aktualizacji) .
RosieC
7

Jest to pułapka, w którą wpadamy , gdy używamy problemu z zabawką, a następnie stawiamy tylko rozwiązania dla szaleńca , gdy próbujemy zilustrować prawdziwy problem.

W podanym przykładzie nie ma żadnej różnicy, czy podane wartości są zakodowane jako wartości wbudowane, czy zdefiniowane jako stałe.

To otaczający kod sprawi, że przykład stanie się horrorem w utrzymaniu i kodowaniu. Jeśli nie ma żadnego otaczający kod, a następnie fragment jest w porządku, przynajmniej w środowisku stałym refaktoringu. W środowisku, w którym refaktoryzacja zwykle się nie dzieje, opiekunowie tego kodu są już martwi, z powodów, które wkrótce staną się oczywiste.

Widzicie, jeśli jest wokół niego kod, wtedy wyraźnie zdarzają się złe rzeczy.

Pierwszą złą rzeczą jest to, że wartość 50000 przybiera gdzieś inną wartość, powiedzmy, kwotę księgową, nad którą zmienia się stawka podatku w niektórych stanach ... wtedy, gdy nastąpi zmiana, opiekun nie ma możliwości dowiedzieć się, kiedy je znajdzie dwa wystąpienia 50000 w kodzie, niezależnie od tego, czy oznaczają to samo 50 000, czy całkowicie 50 000 niezwiązanych ze sobą. A czy powinieneś także szukać 49999 i 50001, na wypadek, gdyby ktoś użył ich również jako stałych? Nie jest to wezwanie do umieszczenia tych zmiennych w pliku konfiguracyjnym oddzielnej usługi: ale wbudowanie ich na stałe jest oczywiście błędne. Zamiast tego powinny to być stałe, zdefiniowane i określone w obrębie klasy lub pliku, w którym są używane. Jeśli dwa wystąpienia 50k używają tej samej stałej, wówczas prawdopodobnie stanowią one to samo ograniczenie legislacyjne; jeśli nie, prawdopodobnie nie; i tak czy inaczej, będą mieli imię,

Nazwy plików są przekazywane do funkcji - attachDocument () - która przyjmuje podstawowe nazwy plików jako ciąg, bez ścieżki lub rozszerzenia. Nazwy plików to w zasadzie klucze obce do jakiegoś systemu plików lub bazy danych, lub skądkolwiek attachDocument () pobiera pliki. Ale ciągi nic o tym nie mówią - ile jest plików? Jakie to są typy plików? Skąd wiesz, kiedy otwierając się na nowy rynek, czy musisz zaktualizować tę funkcję? Do jakich rzeczy można je przyczepić? Opiekun pozostaje całkowicie w ciemności, a wszystko, co ma, to ciąg znaków, który może pojawić się wiele razy w kodzie i oznaczać różne rzeczy za każdym razem, gdy się pojawia. W jednym miejscu „SR008-04X” to oszukiwany kod. W innym poleceniem jest zamówienie czterech rakiet wspomagających SR008. Tutaj to jest nazwa pliku? Czy są one powiązane? Ktoś właśnie zmienił tę funkcję, aby wspomnieć o innym pliku „KLIENT”. Następnie ty, biedny opiekunu, powiedziano ci, że nazwa pliku „KLIENT” musi zostać zmieniona na „KLIENT”. Ale ciąg „CLIENT” pojawia się 937 razy w kodzie ... gdzie w ogóle zaczynasz szukać?

Problemem zabawka jest, że wartości są niezwykłe i można racjonalnie gwarantowane jest unikatowy w kodzie. Nie „1” lub „10”, ale „50 000”. Nie „klient” ani „raport”, ale „SR008-04X”.

Strawman jest to, że tylko inny sposób na rozwiązanie problemu nieprzeniknione nieprzezroczystych stałych jest ula je w pliku konfiguracyjnym jakiejś niezwiązanej usługi.

Razem możesz użyć tych dwóch błędów, aby udowodnić, że jakikolwiek argument jest prawdziwy.

Dewi Morgan
źródło
2
Nie problem z zabawkami, nie słomianin. Jest to coś, co cały czas będziesz widzieć w tego rodzaju aplikacjach biznesowych. Nie ma „otwarcia się na nowy rynek”, nie ma ponownego użycia tego samego numeru (w końcu i tak miałoby to inne znaczenie), a w każdym razie artykuł nie mówi nic przeciwko DRY - jeśli istnieją dwie zależności od wartości, to zostanie przeniesiony do metody lub stałej. Pokaż przykłady, w jaki sposób należy nazywać te stałe (w ustawieniach konfiguracji, to nie ma znaczenia) i gdzie powinny być przechowywane w sposób, który jest przyszłościowy i bardziej przejrzysty niż kod.
Luaan,
4
Przykład nie załamuje się, ponieważ jest to problem z zabawkami. Otaczający kod zawsze będzie okropny, ponieważ reguły biznesowe, które musi wykonywać oprogramowanie, są przerażające . Próby ominięcia tego fundamentalnego wyzwania za pomocą mechanizmów reguł i DSL, a tym bardziej często prokrastynacja programisty , ponieważ rozwiązywanie problemów CS jest przyjemniejsze niż rozwiązywanie zawiłości formularzy podatkowych. Próby osiągnięcia „elegancji” są często sprawami głupców, ponieważ ostatecznym zadaniem oprogramowania jest modelowanie skomplikowanej katastrofy.
whatsisname
Zasady biznesowe mogą być horrorem, ale samo w sobie nie jest usprawiedliwieniem do pisania tego rodzaju miernego kodu proceduralnego. (Zwykle zgadzam się z Papadimoulis, że łatwiej jest modelować i utrzymywać reguły w kodzie niż w konfiguracji, po prostu uważam, że powinien to być lepszy kod). Problem DRY, który widzę, to nie magiczne liczby, to powtarzające się if (...) { attachDocument(...); }.
David Moles,
2

Jest w tym kilka problemów.

Jednym z problemów jest to, że silnik reguł powinien być zbudowany, aby wszystkie reguły były łatwo konfigurowalne poza samym programem. Odpowiedź w podobnych przypadkach najczęściej brzmi „nie”. Reguły będą się zmieniać w dziwny sposób, trudny do przewidzenia, co oznacza, że ​​silnik reguł musi być rozszerzany za każdym razem, gdy zachodzi zmiana.

Kolejnym problemem jest sposób obsługi tych reguł i ich zmian w kontroli wersji. Najlepszym rozwiązaniem jest podzielenie reguł na klasy dla każdej reguły.

Dzięki temu każda reguła ma swoją własną ważność, niektóre zmieniają się co roku, niektóre zmieniają się w zależności od tego, kiedy wydano pozwolenie lub wystawiono fakturę. Sama reguła zawierająca sprawdzenie, do której wersji ma się zastosować.

Ponieważ stała jest prywatna, nie można jej używać w żadnym innym miejscu w kodzie.

Następnie przygotuj listę wszystkich reguł i zastosuj listę.

Kolejnym problemem jest sposób obsługi stałych. 500000 może wyglądać niepozornie, ale należy bardzo uważać, aby upewnić się, że została poprawnie przekonwertowana. Jeśli zastosowana zostanie dowolna arytmetyka zmiennoprzecinkowa, może zostać przekonwertowana na 500 000,00001, więc porównanie z 500 000,00 000 może się nie powieść. Lub jeszcze gorzej, 500000 zawsze działa zgodnie z przeznaczeniem, ale w jakiś sposób 565000 ulega awarii po konwersji. Upewnij się, że konwersja jest jawna i wykonana przez Ciebie, a nie przez zgadywanie kompilatora. Często odbywa się to poprzez konwersję do BigInteger lub BigDecimal przed użyciem.

Zgięty
źródło
2

Chociaż nie jest to bezpośrednio wspomniane w pytaniu, chciałbym zauważyć, że ważne jest, aby nie zakopać logiki biznesowej w kodzie.

Kod, jak w przykładzie powyżej, który koduje określone zewnętrznie wymogów biznesowych powinien naprawdę żyją w odrębnej części drzewa źródłowego, być może o nazwie businesslogiclub coś podobnego, i należy zachować ostrożność w celu zapewnienia, że tylko koduje wymagań biznesowych jak prosto, czytelnie i zwięźle, jak to możliwe, z minimum płyty kotłowej oraz z jasnymi i pouczającymi komentarzami.

To powinno nie być mieszany z „infrastruktury” kodu, który implementuje funkcjonalność potrzebną do przeprowadzenia logiki biznesowej, takich jak, powiedzmy, realizacji attachDocument()sposobu w przykładzie, lub np UI, rejestrowanie lub kod bazy danych w ogóle. Chociaż jednym ze sposobów wymuszenia tego rozdzielenia jest „miękki kod” całej logiki biznesowej w pliku konfiguracyjnym, nie jest to jedyna (lub najlepsza) metoda.

Taki kod logiki biznesowej powinien być również napisany wystarczająco jasno, aby pokazać go ekspertowi w dziedzinie biznesu bez umiejętności kodowania. Przynajmniej, jeśli i kiedy zmieniają się wymagania biznesowe, kodujący je kod powinien być wystarczająco przejrzysty, aby nawet nowy programista bez wcześniejszej znajomości bazy kodu mógł łatwo zlokalizować, przejrzeć i zaktualizować logikę biznesową, zakładając, że nie jest wymagana żadna nowa jakościowo funkcja.

Najlepiej byłoby, gdyby taki kod został napisany w języku specyficznym dla domeny w celu wymuszenia oddzielenia logiki biznesowej od podstawowej infrastruktury, ale może to być niepotrzebnie skomplikowane w przypadku podstawowej aplikacji wewnętrznej. To powiedziawszy, jeśli np. Sprzedajesz oprogramowanie wielu klientom, którzy potrzebują własnego niestandardowego zestawu reguł biznesowych, prosty język skryptowy specyficzny dla domeny (być może np. Oparty na piaskownicy Lua ) może być właśnie taki.

Ilmari Karonen
źródło
Właśnie o tym myślałem !!! Kiedy logika jest zakopana głęboko w kodzie, w jaki sposób ekspert w dziedzinie / dziedzinie lub użytkownik biznesowy może zobaczyć wartości i logikę, które są używane, aby upewnić się, że mają rację, i zdiagnozować zachowanie systemu? Plik konfiguracyjny powoduje, że ustawienia są widoczne . Muszą istnieć pewne sposoby promowania widoczności reguł biznesowych - nawet jeśli sprawia to, że kodowanie jest „trudniejsze”. Mogę zaakceptować cienką klasę lub zestaw klas, które wykonują zadanie, bez mieszania się z innymi problemami - o ile użytkownik biznesowy ma środki, aby uzyskać do nich dostęp i je zrozumieć.
ErikE 11.04.16