Unconditional Programming, czyli if to zło

Struktury sterujące (if, switch) są źródłem wielu błędów. (Że if to złooo można poczytać na Anti-IF Campaign.) Jeśli można to warto ich unikać. Pokażę prosty przykład.

Funkcja ma za zadanie pobrać n początkowych elementów tablicy. Dodatkowo jeśli tablica jest krótsza niż n to trzeba wziąć tyle ile jest a resztę dopełnić zerami.

IEnumerable<int> PaddedTake(int[] array, int n)
{
    if (n <= array.Count())
    {
        return array.Take(n);
    }
    else
    {
        return array.Concat(Enumerable.Repeat(0, n - array.Count()));
    }
}

Można całkowicie wyeliminować if’a z tego kodu.

IEnumerable<int> PaddedTakeImproved(int[] array, int n)
{
    return Pad(array, n).Take(n);
}

IEnumerable<int> Pad(int[] array, int n)
{
    var paddingLength = Math.Max(0, n - array.Length);
    return array.Concat(Enumerable.Repeat(0, paddingLength));
}

Będziemy zawsze rozszerzać tablicę czasem jednak będzie to o zero elementów. Jest to Null Object Pattern. Dotychczas myślałem o tym wzorcu jako o specjalnej klasie, która ma jakieś nic nie robiące metody. Okazuje się jednak, że może to być prostsze.

Ulepszony kod to nie jest jakaś purystyczna fanaberia, często staram się pisać w ten sposób.

Tłumaczenie i dostosowanie do C# z wpisu Michaela Feathersa pod tytułem Unconditional Programming (ja bardzo skróciłem rozważania).

Reklamy
Ten wpis został opublikowany w kategorii Programowanie i oznaczony tagami , , . Dodaj zakładkę do bezpośredniego odnośnika.

5 odpowiedzi na „Unconditional Programming, czyli if to zło

  1. msm pisze:

    Czy na pewno to pierwsze jest gorsze?
    1) Jest krótsze
    2) Widać od razu co sie dzieje
    3) Ostatecznie, jest szybsze

    `Ulepszony kod to nie jest jakaś purystyczna fanaberia` – polemizowałbym. Piszemy w C# a nie w Ruby, języki (i najlepsze sposoby rozwiązywania problemów w nich) się różnią. W Haskellu za to np. naturalne byłoby `take n (lst ++ repeat 0)`.

    Nie słyszałem o AntiIfCampaign, ale w unikaniu ifów chodzi raczej o stosowanie strategii/podobnych tam gdzie pasują, a nie nabożnym wstręcie do tych dwóch literek.

    Podobny post, nieco mniej ekstremalny: http://koziolekweb.pl/2011/10/26/ekstremalna-obiektowosc-w-praktyce-%E2%80%93-czesc-2-nie-uzywaj-slowa-kluczowego-else/

    PS. Jak już o szybkości mowa, mówimy stanowcze NIE! dla `array.Count()` (powinno być array.Length) skoro wiemy że to tablica.

  2. somekind pisze:

    1) Po co wołać Count() na int[]? Nie prościej użyć Length?
    2) Po co dwie metody i dodatkowy kod, skoro wystarczyłoby:
    static IEnumerable PaddedTake2(int[] array, int n)
    {
    return array.Take(n).Concat(Enumerable.Repeat(0, Math.Max(n – array.Length, 0)));
    }

  3. Count() vs Length – macie rację. Najpierw array było typu IEnumerable a dopiero potem dla prostoty zmieniłem na int[] i zapomniałem update’ować Count().

  4. @somekind Może być jedna metoda. Ja starałem się być jakoś tam w zgodzie z wpisem na którym się wzorowałem i do którego linkuję.
    Kwestia smaku czy lepiej zmieścić całą logikę w jednej linijce. Nie zawsze to jest lepsze i w tym przypadku wydaje mi się mniej czytelne.

  5. @msm w unikaniu if’ów chodzi o to by ich po prostu unikać i pasuje do tego zastsowanie Null Object Pattern nawet w tak trywialnym przykładzie jak ewentualna pusta tablica.
    Wpis na który się powołujesz jest bardzo dobry i ja dodaję do niego kolejną technikę pozbywania się ifów (coś prostszego niż opisywany przykład).
    Odnosząc się do punktów:
    Czy na pewno to pierwsze jest gorsze?
    1) Jest krótsze – preferuję oczywiście krótsze konstrukcje (KISS)ale tylko jeśli są bardziej czytelne
    2) Widać od razu co sie dzieje – tak jak pisze Feathers – na początku jest jeden if, ale dochodzą następne itp, a ostatecznie zrobi nam się taka ładna strzałeczka 🙂
    3) Ostatecznie, jest szybsze – punkty powyższe muszą rozstrzygnąć. Prawie nigdy nie chcemy w takiej sytuacji optymalizować.

Możliwość komentowania jest wyłączona.