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
, const
lub 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?
Odpowiedzi:
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.
Tego rodzaju kod jest zwykle chroniony przez fakt, że sam kod prawdopodobnie ma indywidualne mapowanie do wymagań; tzn. gdy programista wie, że
500000
liczba 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
500000
pojawia 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ącyconst
wartość może nie zdawać sobie sprawy, że500000
jest 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.
źródło
Those requirements are typically owned and maintained by business analysts and subject matter experts, rather than by engineers
co 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.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.
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.
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.
źródło
if
oparty jest na innej zmiennej! Jeśli potrzebna zmienna nie jest dostępna w konfiguracji, musisz zmodyfikować oprogramowanie.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.
Może również umieściłbyś kwotę w księdze?
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”
który zastąpiłbyś na stałe lub bardziej konfigurowalny
kiedy zmieniają się wymagania prawne lub biznesowe.
źródło
if
instrukcji, 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ą są .Wyrazem tego jest (i mogę argumentować, że nawet komentarz jest zbędny):
To tylko powtarzanie tego, co robi kod:
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:
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.
źródło
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,if
warunek i komentarz przestaną być zsynchronizowane. Przeciwnie, stałaLEDGER_AMOUNT_REQUIRING_AUTHLDG1A
nigdy nie zsynchronizuje się z samym sobą i wyjaśnia swój cel bez niepotrzebnego komentarza.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.)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.attachIfNecessary()
metodą i po prostu zapętliłem wszystkie.Pozostałe odpowiedzi są poprawne i przemyślane. Ale oto moja krótka i słodka odpowiedź.
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.
źródło
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.
źródło
if (...) { attachDocument(...); }
.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.
źródło
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
businesslogic
lub 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.
źródło