Jak mogę edytować łańcuch instrukcji if-else, jeśli są zgodne z zasadami Clean Code firmy Uncle Bob?


45

Staram się stosować do czystego kodu wuja Boba, a konkretnie, aby metody były krótkie.

Nie mogę jednak skrócić tej logiki:

if (checkCondition()) {addAlert(1);}
else if (checkCondition2()) {addAlert(2);}
else if (checkCondition3()) {addAlert(3);}
else if (checkCondition4()) {addAlert(4);}

Nie mogę usunąć pozostałych i tym samym podzielić całej rzeczy na mniejsze części, ponieważ „else” w „else if” pomaga wydajności - ocena tych warunków jest droga i jeśli mogę uniknąć oceny poniższych warunków, spowoduję, że jeden z pierwszych to prawda, chcę ich uniknąć.

Nawet semantycznie rzecz biorąc, ocena następnego warunku, jeśli poprzednie zostało spełnione, nie ma sensu z biznesowego punktu widzenia.


edycja: To pytanie zostało zidentyfikowane jako możliwy duplikat eleganckich sposobów radzenia sobie, jeśli (jeśli inaczej) inaczej .

Uważam, że jest to inne pytanie (można to zobaczyć również poprzez porównanie odpowiedzi na te pytania).


46
Co tak naprawdę jest błędne lub niejasne w tym kodzie w kontekście? Nie widzę, jak można to skrócić lub uprościć! Kod oceniający warunki wydaje się już dobrze uwzględniony, podobnie jak metoda wywoływana w wyniku decyzji. Wystarczy spojrzeć na niektóre z poniższych odpowiedzi, które jedynie komplikują kod!
Steve

38
Nie ma nic złego w tym kodzie. Jest bardzo czytelny i łatwy do naśladowania. Wszystko, co zrobisz, aby to jeszcze bardziej zmniejszyć, spowoduje zwiększenie pośrednictwa i utrudni zrozumienie.
17 z 26

20
Twój kod jest w porządku. Włóż pozostałą energię w coś bardziej produktywnego niż próba jej dalszego skrócenia.
Robert Harvey

5
Jeśli to naprawdę tylko 4 warunki, to jest w porządku. Jeśli tak naprawdę jest to 12 lub 50, prawdopodobnie chcesz refaktoryzować na wyższym poziomie niż ta jedna metoda.
JimmyJames

9
Zostaw swój kod dokładnie tak, jak jest. Słuchaj, co zawsze mówili ci twoi rodzice: nie ufaj żadnym wujkom oferującym słodycze dzieciom na ulicy. @Harvey Zabawne, różne próby „ulepszenia” kodu sprawiły, że był on znacznie większy, bardziej skomplikowany i mniej czytelny.
gnasher729

Odpowiedzi:


81

Idealnie uważam, że powinieneś wyodrębnić swoją logikę, aby wprowadzić kod / numer alertu do własnej metody. Twój istniejący kod został zredukowany do

{
    addAlert(GetConditionCode());
}

i masz GetConditionCode () enkapsuluje logikę sprawdzania warunków. Może także lepiej użyć Enum niż magicznej liczby.

private AlertCode GetConditionCode() {
    if (CheckCondition1()) return AlertCode.OnFire;
    if (CheckCondition2()) return AlertCode.PlagueOfBees;
    if (CheckCondition3()) return AlertCode.Godzilla;
    if (CheckCondition4()) return AlertCode.ZombieSharkNado;
    return AlertCode.None;
}

2
Jeśli to możliwe, aby opisać tak, jak to opisujesz (podejrzewam, że tak nie jest, myślę, że OP pomija zmienne dla uproszczenia), nie zmienia kodu, co samo w sobie jest w porządku, ale dodaje ergonomii kodu i trochę czytelności + 1
op

17
Za pomocą tych kodów alarmowych dziękuję tylko jednemu kodowi, który może zostać zwrócony naraz
Josh Part

12
Wydaje się to również idealnym dopasowaniem do użycia instrukcji switch - jeśli jest ona dostępna w języku OP.
Frank Hopkins,

4
Prawdopodobnie dobrym pomysłem jest wyodrębnienie kodu błędu do nowej metody, jeśli można go zapisać tak, aby był użyteczny w wielu sytuacjach bez konieczności podawania wielu informacji na temat konkretnej sytuacji. W rzeczywistości jest to kompromis i próg rentowności, kiedy warto. Ale dość często widać, że sekwencja walidacji jest specyficzna dla danego zadania i lepiej trzymać je razem z tym zadaniem. W takich przypadkach wymyślenie nowego typu w celu poinformowania innej części kodu, co należy zrobić, jest niepożądanym balastem.
PJTraill

6
Jednym z problemów związanych z tym ponownym wdrożeniem jest to, że funkcja addAlertmusi sprawdzać stan fałszywego alertu AlertCode.None.
David Hammen

69

Ważnym pomiarem jest złożoność kodu, a nie wielkość bezwzględna. Zakładając, że różne warunki są tak naprawdę pojedynczymi wywołaniami funkcji, podobnie jak działania nie są bardziej złożone niż to, co pokazałeś, powiedziałbym, że nie ma nic złego w kodzie. To jest już tak proste, jak to tylko możliwe.

Każda próba dalszego „uproszczenia” naprawdę skomplikuje sprawę.

Oczywiście możesz zastąpić elsesłowo kluczowe na, returnjak sugerowali inni, ale to tylko kwestia stylu, a nie zmiana złożoności.


Na bok:

Moja ogólna rada brzmiałaby: nigdy nie upominaj się o żadnej zasadzie czystego kodu: większość porad dotyczących kodowania, które widzisz w Internecie, jest dobra, jeśli zastosujesz je w odpowiednim kontekście, ale radykalne zastosowanie tej samej porady wszędzie może wygrać wpis w IOCCC . Sztuka polega na tym, aby zawsze znaleźć równowagę, która pozwoli ludziom łatwo zrozumieć kod.

Używaj zbyt dużych metod, a jesteś wkręcony. Użyj zbyt małych funkcji, a będziesz wkręcony. Unikaj wyrażeń potrójnych, a jesteś przykręcony. Wszędzie używaj wyrażeń potrójnych, a ty jesteś przykręcony. Uświadom sobie, że są miejsca, które wymagają funkcji jednowierszowych i miejsca, które wymagają funkcji 50-liniowych (tak, istnieją!). Uświadom sobie, że są miejsca, które wymagają if()wyciągów i że są miejsca, które wymagają ?:operatora. Skorzystaj z pełnego arsenału, który jest do Twojej dyspozycji, i zawsze staraj się używać najlepiej dopasowanego narzędzia, jakie możesz znaleźć. I pamiętajcie, nie bądźcie religijni nawet na temat tej rady.


2
Argumentowałbym, że zamiana else ifna wewnętrzny, returnpo którym następuje prosty if(usunięcie else), może utrudnić odczytanie kodu . Kiedy kod mówi else if, od razu wiem, że kod w następnym bloku zostanie wykonany tylko wtedy, gdy poprzedni nie. Bez musów, bez zamieszania. Jeśli jest to zwykły if, może on wykonać lub nie, niezależnie od tego, czy poprzedni został wykonany. Teraz będę musiał poświęcić trochę wysiłku umysłowego, aby przeanalizować poprzedni blok, aby zauważyć, że kończy się on na return. Wolę poświęcić ten wysiłek umysłowy na analizowanie logiki biznesowej.
CVn

1
Wiem, to drobiazg, ale przynajmniej dla mnie else iftworzy jedną jednostkę semantyczną. (To niekoniecznie jest pojedyncza jednostka dla kompilatora, ale to jest w porządku.) ...; return; } if (...Nie; nie mówiąc już o tym, że jest rozłożony na wiele linii. To jest coś, na co naprawdę muszę spojrzeć, aby zobaczyć, co robi, zamiast być w stanie wziąć to bezpośrednio, widząc tylko parę słów kluczowych else if.
CVn

@ MichaelKjörling Full Ack. Ja wolałbym else ifkonstrukcję, zwłaszcza że jej łańcuchowa forma jest tak dobrze znanym wzorem. Jednak kod formularza if(...) return ...;jest również dobrze znanym wzorcem, więc nie do końca bym tego potępił. Uważam to jednak za drobny problem: logika przepływu sterowania jest taka sama w obu przypadkach, a jedno bliższe spojrzenie na if(...) { ...; return; }drabinę powie mi, że rzeczywiście jest równoważne else ifdrabinie. Widzę strukturę pojedynczego terminu, wywnioskuję jego znaczenie, zdaję sobie sprawę, że jest powtarzany wszędzie i wiem, co jest grane.
cmaster

Pochodząc z JavaScript / node.js, niektórzy używaliby kodu „pas i szelki”, używając zarówno else if i return . np.else if(...) { return alert();}
user949300,

1
„I pamiętajcie, nie bądźcie religijni, nawet jeśli chodzi o tę radę”. +1
Słowa jak Jared

22

Kontrowersyjne jest, czy jest to „lepsze” niż zwykłe, jeśli… w danym przypadku. Ale jeśli chcesz spróbować czegoś innego, jest to powszechny sposób na zrobienie tego.

Umieść swoje warunki w obiektach i umieść te obiekty na liście

foreach(var condition in Conditions.OrderBy(i=>i.OrderToRunIn))
{
    if(condition.EvaluatesToTrue())
    {
        addAlert(condition.Alert);
        break;
    }
}

Jeśli pod warunkiem jest wymaganych wiele akcji, możesz wykonać szaloną rekursję

void RunConditionalAction(ConditionalActionSet conditions)
{
    foreach(var condition in conditions.OrderBy(i=>i.OrderToRunIn))
    {
        if(condition.EvaluatesToTrue())
        {
            RunConditionalAction(condition);
            break;
        }
    }
}

Z pewnością tak. Działa to tylko wtedy, gdy masz wzorzec logiki. Jeśli spróbujesz wykonać super ogólną rekursywną akcję warunkową, wówczas konfiguracja obiektu będzie tak skomplikowana jak oryginalna instrukcja if. Będziesz wymyślał swój nowy język / framework.

Ale Twój przykład nie mają wzór

Typowym przypadkiem użycia tego wzorca byłaby walidacja. Zamiast :

bool IsValid()
{
    if(condition1 == false)
    {
        throw new ValidationException("condition1 is wrong!");
    }
    elseif(condition2 == false)
    {
    ....

}

Staje się

[MustHaveCondition1]
[MustHaveCondition2]
public myObject()
{
    [MustMatchRegExCondition("xyz")]
    public string myProperty {get;set;}
    public bool IsValid()
    {
        conditions = getConditionsFromReflection()
        //loop through conditions
    }
}

27
To tylko przenosi if...elsedrabinę do konstrukcji Conditionslisty. Zysk netto jest ujemny, ponieważ konstrukcja Conditionszajmie tyle samo kodu, co kod OP, ale dodatkowa pośrednia obciąża czytelność. Zdecydowanie wolałbym czystą kodowaną drabinę.
cmaster

3
@cmaster tak Myślę, że powiedziałem dokładnie to "wtedy konfiguracja obiektu będzie tak skomplikowana, jak oryginalna instrukcja if
Ewan

7
Jest to mniej czytelne niż oryginał. Aby dowiedzieć się, jaki warunek jest sprawdzany, musisz zacząć kopać w innym obszarze kodu. Dodaje niepotrzebny poziom pośredni, który sprawia, że ​​kod jest trudniejszy do zrozumienia.
17 z 26

8
Konwersja łańcucha if… else if… else… do tabeli predykatów i działań ma sens, ale tylko dla znacznie większych przykładów. Tabela dodaje pewnej złożoności i pośrednictwa, więc potrzebujesz wystarczającej liczby wpisów, aby zamortyzować to narzut koncepcyjny. Tak więc, dla 4 par predykatów / akcji, zachowaj prosty oryginalny kod, ale jeśli masz 100, zdecydowanie idź z tabelą. Punkt podziału znajduje się gdzieś pośrodku. @cmaster, tabelę można zainicjować statycznie, więc przyrostowy koszt dodawania pary predykatów / akcji to jedna linia, która je tylko nazywa: trudno jest lepiej.
Stephen C. Steel

2
Czytelność NIE jest osobista. Jest to obowiązek publicznego programowania. To jest subiektywne. Właśnie dlatego ważne jest, aby przyjeżdżać do takich miejsc i słuchać, co społeczeństwo programistyczne ma do powiedzenia na ten temat. Osobiście uważam ten przykład za niekompletny. Pokaż, jak conditionsjest skonstruowany ... ARG! Nie atrybuty adnotacji! Boże czemu? Ow oczy!
candied_orange

7

Rozważ użycie return;po spełnieniu jednego warunku, oszczędza ci to wszystko else. Możesz nawet być w stanie return addAlert(1)bezpośrednio, jeśli ta metoda ma wartość zwracaną.


3
Oczywiście zakłada to, że nic więcej się nie dzieje po łańcuchu ifs ... To może być rozsądne założenie, a potem może nie być.
CVn

5

Czasami widziałem takie konstrukcje uważane za czystsze:

switch(true) {
    case cond1(): 
        statement1; break;
    case cond2():
        statement2; break;
    case cond3():
        statement3; break;
    // .. etc
}

Trójskładnik z odpowiednimi odstępami może być również zgrabną alternatywą:

cond1() ? statement1 :
cond2() ? statement2 :
cond3() ? statement3 : (null);

Sądzę, że możesz także spróbować utworzyć tablicę z parą zawierającą warunek i funkcję i iterować nad nim do momentu spełnienia pierwszego warunku - co, jak widzę, byłoby równe pierwszej odpowiedzi Ewana.


1
trójka jest czysta
Ewan

6
@Ewan debugowanie zepsutego „głęboko rekurencyjnego trójskładnika” może być niepotrzebnym bólem.
dri

5
to wygląda schludny na ekranie chociaż.
Ewan

Uhm, w jakim języku można używać funkcji z caseetykietami?
undercat

1
@undercat to poprawny afaik ECMAScript / JavaScript
zworek

1

Jako wariant odpowiedzi @ Ewan, możesz stworzyć łańcuch (zamiast „płaskiej listy”) takich warunków:

abstract class Condition {
  private static final  Condition LAST = new Condition(){
     public void alertOrPropagate(DisplayInterface display){
        // do nothing;
     }
  }
  private Condition next = Last;

  public Condition setNext(Condition next){
    this.next = next;
    return this; // fluent API
  }

  public void alertOrPropagate(DisplayInterface display){
     if(isConditionMeet()){
         display.alert(getMessage());
     } else {
       next.alertOrPropagate(display);
     }
  }
  protected abstract boolean isConditionMeet();
  protected abstract String getMessage();  
}

W ten sposób możesz zastosować swoje warunki w określonej kolejności, a infrastruktura (pokazana klasa abstrakcyjna) pomija pozostałe kontrole po spełnieniu pierwszego.

Jest to miejsce, w którym przewyższa podejście oparte na „płaskiej liście”, gdzie musisz zaimplementować „pomijanie” w pętli, która stosuje warunki.

Po prostu konfigurujesz łańcuch warunków:

Condition c1 = new Condition1().setNext(
  new Condition2().setNext(
   new Condition3()
 )
);

I rozpocznij ocenę od prostego połączenia:

c1.alertOrPropagate(display);


4
Nie będę udawać, że mówię za nikogo innego, ale chociaż kod w pytaniu jest natychmiast czytelny i oczywisty w swoim zachowaniu, nie uważałbym, że to od razu oczywiste, co robi.
CVn

0

Po pierwsze, oryginalny kod nie jest straszny IMO. Jest to całkiem zrozumiałe i nie ma w tym nic złego.

Jeśli więc ci się nie podoba, skorzystaj z pomysłu @ Ewana, by użyć listy, ale usuń nieco nienaturalny foreach breakwzór:

public class conditions
{
    private List<Condition> cList;
    private int position;

    public Condition Head
    {
        get { return cList[position];}
    }

    public bool Next()
    {
        return (position++ < cList.Count);
    }
}


while not conditions.head.check() {
  conditions.next()
}
conditions.head.alert()

Teraz dostosuj to w wybranym języku, spraw, aby każdy element listy stał się obiektem, krotką, cokolwiek, i jesteś dobry.

EDYCJA: wygląda na to, że nie jest tak jasne, jak myślałem, więc pozwól mi wyjaśnić dalej. conditionsjest jakąś uporządkowaną listą; headjest bieżącym badanym elementem - na początku jest to pierwszy element listy i za każdym razem, gdy next()jest wywoływany, staje się kolejnym; check()i alert()to checkConditionX()i addAlert(X)z PO.


1
(Nie głosowałem, ale) Nie mogę tego zrobić. Co to jest głowa ?
Belle-Sophie,

@Belle Zredagowałem odpowiedź, aby wyjaśnić dalej. To ten sam pomysł, co Ewana, ale z while notzamiast foreach break.
Nico,

Genialna ewolucja genialnego pomysłu
Ewan

0

Pytanie nie ma pewnych szczegółów. Jeśli warunki są następujące:

  • może ulec zmianie lub
  • powtórzone w innych częściach aplikacji lub systemu lub
  • zmodyfikowane w niektórych przypadkach (takie jak różne kompilacje, testy, wdrożenia)

lub jeśli zawartość addAlertjest bardziej skomplikowana, prawdopodobnie lepszym rozwiązaniem, powiedzmy c #, byłoby:

//in some central spot
IEnumerable<Tuple<Func<bool>, int>> Conditions = new ... {
  Tuple.Create(CheckCondition1, 1),
  Tuple.Create(CheckCondition2, 2),
  ...
}

//at the original place
var matchingCondition = Conditions.Where(c=>c.Item1()).FirstOrDefault();
if(matchingCondition != null) 
  addAlert(matchingCondition.Item2)

Krotki nie są tak piękne w c # <8, ale zostały wybrane dla wygody.

Zalety tej metody, nawet jeśli żadna z powyższych opcji nie ma zastosowania, polega na tym, że struktura jest typowana statycznie. Nie możesz przypadkowo spieprzyć, powiedzmy, pominięcie else.


0

Najlepszym sposobem na zmniejszenie złożoności Cyclomatic w przypadkach, gdy jest ich dużo, if->then statementsjest użycie słownika lub listy (zależnej od języka) do przechowywania wartości klucza (jeśli wartość instrukcji lub pewnej wartości), a następnie wyniku wartość / funkcja.

Na przykład zamiast (C #):

if (i > 10) { return "Two"; }
else if (i > 8) { return "Four" }
else if (i > 4) { return "Eight" }
return "Ten";  //etc etc say anything after 3 or 4 values

Mogę po prostu

var results = new Dictionary<int, string>
{
  { 10, "Two" },
  { 8, "Four"},
  { 4, "Eight"},
  { 0, "Ten"},
}

foreach(var key in results.Keys)
{
  if (i > results[key]) return results.Values[key];
}

Jeśli używasz bardziej nowoczesnych języków, możesz zapisać więcej logiki niż tylko wartości (c #). To są naprawdę tylko funkcje wbudowane, ale możesz również wskazać inne funkcje, jeśli logika ma zawężać do wbudowania.

var results = new Dictionary<Func<int, bool>, Func<int, string>>
{
  { (i) => return i > 10; ,
    (i) => return i.ToString() },
  // etc
};

foreach(var key in results.Keys)
{ 
  if (key(i)) return results.Values[key](i);
}

0

Staram się stosować do czystego kodu wuja Boba, a konkretnie, aby metody były krótkie.

Nie mogę jednak skrócić tej logiki:

if (checkCondition()) {addAlert(1);}
else if (checkCondition2()) {addAlert(2);}
else if (checkCondition3()) {addAlert(3);}
else if (checkCondition4()) {addAlert(4);}

Twój kod jest już za krótki, ale sama logika nie powinna być zmieniana. Na pierwszy rzut oka wygląda na to, że powtarzasz się z czterema wywołaniami checkCondition(), a po dokładnym przeczytaniu kodu każde z nich jest inne. Należy dodać prawidłowe formatowanie i nazwy funkcji, np .:

if (is_an_apple()) {
  addAlert(1);
}
else if (is_a_banana()) {
  addAlert(2);
}
else if (is_a_cat()) {
  addAlert(3);
}
else if (is_a_dog()) {
  addAlert(4);
}

Twój kod powinien być czytelny przede wszystkim. Po przeczytaniu kilku książek wuja Boba uważam, że jest to przesłanie, które konsekwentnie próbuje przekazać.


0

Załóżmy, że wszystkie funkcje są zaimplementowane w tym samym komponencie, możesz sprawić, że funkcje zachowają pewien stan, aby pozbyć się wielu gałęzi w przepływie.

EG: checkCondition1()stałby się evaluateCondition1(), na którym sprawdzałby, czy spełniono poprzedni warunek; jeśli tak, to buforuje pewną wartość do odzyskania getConditionNumber().

checkCondition2()stanie się evaluateCondition2(), na którym sprawdzi, czy poprzednie warunki zostały spełnione. Jeśli poprzedni warunek nie został spełniony, sprawdza scenariusz warunku 2, buforując wartość do pobrania getConditionNumber(). I tak dalej.

clearConditions();
evaluateCondition1();
evaluateCondition2();
evaluateCondition3();
evaluateCondition4();
if (anyCondition()) { addAlert(getConditionNumber()); }

EDYTOWAĆ:

Oto, jak należałoby wdrożyć kontrolę drogich warunków, aby to podejście zadziałało.

bool evaluateCondition34() {
    if (!anyCondition() && A && B && C) {
        conditionNumber = 5693;
        return true;
    }
    return false;
}

...

bool evaluateCondition76() {
    if (!anyCondition() && !B && C && D) {
        conditionNumber = 7658;
        return true;
    }
    return false;
}

Dlatego jeśli masz zbyt wiele drogich kontroli do wykonania, a elementy w tym kodzie pozostają prywatne, takie podejście pomaga w utrzymaniu go, umożliwiając w razie potrzeby zmianę kolejności kontroli.

clearConditions();
evaluateCondition10();
evaluateCondition9();
evaluateCondition8();
evaluateCondition7();
...
evaluateCondition34();
...
evaluateCondition76();

if (anyCondition()) { addAlert(getConditionNumber()); }

Ta odpowiedź stanowi tylko alternatywną sugestię z innych odpowiedzi i prawdopodobnie nie będzie lepsza niż oryginalny kod, jeśli weźmiemy pod uwagę tylko 4 wiersze kodu. Chociaż nie jest to okropne podejście (i żadne nie utrudnia konserwacji, jak powiedzieli inni), biorąc pod uwagę wspomniany scenariusz (zbyt wiele kontroli, tylko główna funkcja ujawniona jako publiczna, wszystkie funkcje są szczegółami implementacji tej samej klasy).


Nie podoba mi się ta sugestia - ukrywa ona logikę testowania w wielu funkcjach. Może to utrudnić utrzymanie kodu, jeśli na przykład trzeba zmienić kolejność i zrobić # 3 przed # 2.
Lawrence

Nie. Możesz sprawdzić, czy jakiś poprzedni warunek został oceniony, jeśli anyCondition() != false.
Emerson Cardoso

1
Ok, rozumiem o co ci chodzi. Jeśli jednak (powiedzmy) warunki 2 i 3 truezostaną ocenione , PO nie chce oceny warunku 3.
Lawrence

Miałem na myśli to, że możesz sprawdzić anyCondition() != falsew ramach funkcji evaluateConditionXX(). Jest to możliwe do wdrożenia. Jeśli podejście polegające na użyciu stanu wewnętrznego nie jest pożądane, rozumiem, ale argument, że to nie działa, jest nieważny.
Emerson Cardoso

1
Tak, moim zastrzeżeniem jest to, że niepomocnie ukrywa logikę testowania, a nie to, że nie może działać. W twojej odpowiedzi (ust. 3) czek na spełnienie warunku 1 jest umieszczony w eval ... 2 (). Ale jeśli zmieni warunki 1 i 2 na najwyższym poziomie (z powodu zmian wymagań klienta itp.), Będziesz musiał przejść do eval ... 2 (), aby usunąć sprawdzanie warunku 1, a także przejść do eval. ..1 (), aby dodać sprawdzanie warunku 2. Można to uruchomić, ale może to łatwo prowadzić do problemów z konserwacją.
Lawrence

0

Więcej niż dwie klauzule „else” zmusza czytelnika kodu do przejścia całego łańcucha w celu znalezienia interesującego go kodu. Użyj metody, takiej jak: void AlertUponCondition (Warunek) {switch (warunek) {case Condition.Con1: ... break; case Condition.Con2: ... przerwa; itp ...} Gdzie „Warunek” jest właściwym wyliczeniem. W razie potrzeby zwróć wartość bool lub wartość. Nazwij to tak: AlertOnCondition (GetCondition ());

To naprawdę nie może być prostsze, I jest szybsze niż łańcuch if-else, gdy przekroczysz kilka przypadków.


0

Nie mogę mówić o twojej konkretnej sytuacji, ponieważ kod nie jest konkretny, ale ...

taki kod często pachnie brakującym modelem OO. Naprawdę masz cztery rodzaje rzeczy, każda związana z własnym typem alertera, ale zamiast rozpoznawać te encje i tworzyć dla nich instancję klasy, traktujesz je jako jedną rzecz i próbujesz nadrobić to później, w czasie musisz wiedzieć, z czym masz do czynienia, aby kontynuować.

Polimorfizm mógł ci bardziej odpowiadać.

Bądź podejrzliwy w stosunku do kodu za pomocą długich metod zawierających długie lub złożone konstrukcje if-then. Często potrzebujesz drzewa klasy z pewnymi wirtualnymi metodami.

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