2012-12-18 14 views
7

Kiedyś szukałem narzędzia do sprawdzania kodu Rubiego i natknąłem się na klejnot pelusa, który wygląda interesująco. Jedną z rzeczy, które sprawdza, jest liczba innych instrukcji użytych w danym pliku Ruby.Dlaczego inne stwierdzenia są zniechęcane w Ruby?

Moje pytanie brzmi, dlaczego te są złe? Rozumiem, że instrukcje if/else często dodają wiele komplikacji (i uważam, że celem jest zmniejszenie złożoności kodu), ale w jaki sposób można sprawdzać metodę, która sprawdza dwa przypadki bez numeru else?

Reasumując, mam dwa pytania:

1) Czy istnieje powód inny niż zmniejszenie złożoności kodu, który można by uniknąć else?

2) Oto przykładowa metoda z aplikacji, nad którą pracuję, która używa instrukcji else. Jak mógłbyś napisać to bez niego? Jedyną opcją, o której mogłem pomyśleć, byłoby potrójne stwierdzenie, ale jest tu dość logiki, że myślę, że potrójne stwierdzenie byłoby bardziej skomplikowane i trudniejsze do odczytania.

def deliver_email_verification_instructions 
    if Rails.env.test? || Rails.env.development? 
    deliver_email_verification_instructions! 
    else 
    delay.deliver_email_verification_instructions! 
    end 
end 

Jeśli napisałeś to z operatorem potrójnego, byłoby:

def deliver_email_verification_instructions 
    (Rails.env.test? || Rails.env.development?) ? deliver_email_verification_instructions! : delay.deliver_email_verification_instructions! 
end 

Czy to prawda? Jeśli tak, to czy nie jest to trudniejsze do odczytania? Czy stwierdzenie else nie pomaga w rozbiciu tego? Czy istnieje inny, lepszy, else -less sposób, aby napisać to, o czym nie myślę?

Sądzę, że szukam tutaj rozważań stylistycznych.

+1

Dobry write-up na 'else' jako zapachy kodu tutaj: http://solnic.eu/2012/04/11/get-rid-of-that-code-smell-control-couple.html – michaelmichael

+0

To całkiem nieźle, a niektóre z tego, czego szukałem (choć trochę nad głową w częściach). Zadbaj o to, aby opublikować to jako odpowiedź, dopracować i uzyskać dodatkowe punkty karmy, uzupełnić mój przykład lub użyć jednego do zilustrowania? Czy uważasz, że post powinien działać jako odpowiedź? – nickcoxdotme

+0

Nadużywanie jest złe, podobnie jak sztywna presja, której należy unikać. Jest czas i miejsce, a przy poprawnie napisanym kodzie, okazjonalnie jest to właściwe rozwiązanie. Może to być mylące z kodem spaghetti, więc unikaj pisania źle przemyślanego kodu, a reszta powinna zająć się sobą. –

Odpowiedz

6

Zacznę od stwierdzenia, że ​​nie ma naprawdę nic złegoz kodem, i generalnie należy mieć świadomość, że niezależnie od jakości narzędzie kod powie Ci może być kompletny nonsens, ponieważ brakuje kontekst, aby ocenić, co faktycznie robisz.

Ale z powrotem do kodu. Jeśli nie było klasy, który miał dokładnie jeden sposób, w którym wystąpił fragment

if Rails.env.test? || Rails.env.development? 
    # Do stuff 
else 
    # Do other stuff 
end 

, który byłby całkowicie w porządku (istnieją zawsze różne podejścia do danej rzeczy, ale nie musisz się o to martwić, nawet jeśli programiści będą cię nienawidzić za to, że nie kłócisz się z nimi na ten temat: D).

Teraz jest trudna część. Ludzie są leniwi jak diabli i dlatego fragmenty kodu, takie jak powyższe, są łatwym celem dla kodowania kopiowania/wklejania (dlatego ludzie będą twierdzić, że należy ich unikać, ponieważ jeśli rozwiniesz klasę później, jesteś bardziej prawdopodobny po prostu kopiować i wklejać rzeczy, niż je faktycznie naprawiać).

Spójrzmy na przykład na fragment kodu. Zasadniczo proponuję to samo co @Mik_Die, jednak jego przykład jest równie podatny na kopiowanie/wklejanie jak twój. W związku z tym, że należy zrobić (IMO) jest taka:

class Foo 
    def initialize 
    @target = (Rails.env.test? || Rails.env.development?) ? self : delay 
    end 

    def deliver_email_verification_instructions 
    @target.deliver_email_verification_instructions! 
    end 
end 

To nie może być stosowane do aplikacji, jak jest, ale mam nadzieję, że masz pomysł, który jest: dry. Zawsze.Za każdym razem, gdy się powtarzasz, nie tylko sprawiasz, że Twój kod jest mniej łatwy w utrzymaniu, ale w rezultacie także bardziej podatny na błędy w przyszłości, ponieważ jedno lub nawet 99/100 wystąpień tego, co skopiowałeś i wkleiłeś, mógł zostać zmieniony, ale jedna pozostała zjawiskiem jest to, co powoduje, że @disasterOfEpicProportions w końcu :)


Kolejnym punktem, który zapomniałem została wychowana przez @RayToal (dzięki :), który jest, że jeśli/else konstrukcje są często wykorzystywane w kombinacja z parametrami wejściowymi boolowskimi, powodującymi konstrukcje takie jak ta (rzeczywisty kod z projektu, który muszę utrzymywać):

class String 
    def uc(only_first=false) 
    if only_first 
     capitalize 
    else 
     upcase 
    end 
    end 
end 

Zignorujmy oczywiste nazewnictwo metody i problemy z łataniem małpek i skoncentrujmy się na konstrukcji if/else, która jest używana do nadawania różnym zachowaniom metody uc w zależności od parametru only_first. Taki kod jest naruszeniem zasady odpowiedzialności pojedynczej, ponieważ twoja metoda to robienie więcej niż jednej rzeczy, dlatego powinieneś napisać dwie metody na pierwszym miejscu.

+2

To jest najlepsza możliwa do zreaktorowania IMHO kodu OP. Wyrażenia 'if' z częściami' else' zwykle są mile widziane, ponieważ kiedy widzisz je, twoje otaczające metody to ** wykonanie więcej niż jednej rzeczy **, naruszenie SRP (chociaż nigdy bym nie argumentował za podążaniem za tym 100% czas). Inicjalizacja celu w oparciu o środowisko nie powoduje więcej niż jednej rzeczy; oblicza właściwy cel. Wtedy metoda dostawy jest świetna. +1 –

+0

@RayToal Dzięki za dodanie :) Zaktualizowałem moją odpowiedź. – fresskoma

2
def deliver_email_verification_instructions 
    subj = (Rails.env.test? || Rails.env.development?) ? self : delay 
    subj.deliver_email_verification_instructions! 
end 
+1

Solidny refaktoryzacji metody, którą napisałem, co doceniam. Każdy kontekst, który możesz dodać, aby odpowiedzieć na moje pierwsze pytanie, dlaczego wybrałeś ten styl i co ta metoda obiektywnie oferuje jeden z instrukcją "else"? To mogłem oznaczyć poprawnie. – nickcoxdotme