2014-09-10 15 views
5

Więc wiem o kilku różnych podejść, że znam i chcę odkrywać zalety i wady różnych sposobów na różne kryteria, które są:Długi nil bezpieczna metoda łańcuchy

  • czytelność
  • wydajność
  • łatwość debugowania
  • zasady OO (niska i wysoka spójność sprzęgło)

jawnie przy użyciu Sposób próba z aktywnego wsparcia

person.try(:pet).try(:name).try(:upcase) 

Stosując ratowanie zero

person.pet.name.upcase rescue nil 

Użycie łańcucha & & operatora

person && person.pet && person.pet.name && person.pet.name.upcase 

Monkey łatanie klasa obiektu, patrz https://gist.github.com/thegrubbsian/3499234 dla pierwotnego GIST

class Object 

     def try_all(*methods) 
     values = [self] 
     methods.each do |method| 
      value = values.last.try(method) 
      return nil if value.nil? 
      values << value 
     end 
     values.last 
     end 

    end 

person.try_all(:pet, :name, :upcase) 

Nie masz zerowe bezpiecznego kodu, zamiast sprawdzania poprawności danych przed wywołaniem kod

#not a good implementation by any means  
def person_has_pet_with_name? person 
    begin 
    return true if !person.pet.name.nil? 
    rescue 
    return false 
    end 
end 

person.pet.name.upcase if person_has_pet_with_name?(person) 
+0

Czy moja odpowiedź pomogła Ci w jakikolwiek sposób? –

+0

Proszę oznaczyć odpowiedź jako pytanie rozwiązane, jeśli odpowiedziała na twoje pytanie. –

Odpowiedz

4

Moja osobista opinia o małpiej łatania w ogóle to nie rób tego, chyba że nie ma innego sposobu (a nawet niż myślę dwa razy, jeśli naprawdę chcę, żeby łatać małpy).
Poza szynami już zbyt duże obiekty. Więc nie sugerowałbym, aby niestandardowe obiekty były jeszcze większe.
Dlaczego nie unikając złamać Prawo Demeter przez klasyczne podejście Delegator:

class Person 
    attr_accessor :pet 
    delegate :name_upcased, to: :pet, 
    prefix: true, allow_nil: true 
end 

class Pet 
    attr_accessor :name 
    def name_upcased 
    @name.upcase if @name 
    end 
end 

@person.try :pet_name_upcased 

Można również przeczytać o Prawo Demeter na Do not break the law of Demeter! i Module#delegate.
Przynajmniej nie trzymałbym się Object#try tak długo, jak prosty stan rozwiązuje go, ponieważ patrząc na źródło "spróbuj", jest bardziej kosztowne niż warunek.

0

bym uniknąć długich łańcuchów połączeń, jak są one wyraźne naruszenie Law of Demeter:

person.try(:pet).try(:name).try(:upcase) 

Gdyby chcesz przetestować kod i tak jakoś skrótową osobę, swoje testy staną się niezwykle złożony. Sugerowałbym rozwiązanie takie jak poniższe, w którym złożoność tego łańcucha jest podzielona między zaangażowane klasy. Jest to metoda zorientowana na obiekt. Tutaj żadna z klas nie wie zbyt wiele o drugiej.

class Person 
    def upcased_pet_name 
    pet.try(:upcased_name) 
    end 
end 

class Pet 
    def upcased_name 
    name.try(:upcase) 
    end 
end 

# Simple method call. nil checks are handled elsewhere 
person.try(:upcased_pet_name) 

w badaniach, to teraz o wiele łatwiejsze do skrótową person, a kod jest dużo łatwiejsze do odczytania.

+0

Przeczytałem twój blog na temat Object # Spróbuj później. Miły. Ale próba Object # jest bardziej kosztowna niż prosty warunek. Dlatego nie jestem wielkim fanem tego. Więc może nie nadużywać tego, czy warunek może być ukryty? –

+0

Podejście "jeśli" również działało bardzo ładnie, częściowo dlatego, że działa również w prostym języku Ruby. To może być dobre refaktoryzowanie mojego kodu. – fivedigit

0

Problem z prawem Demeter polega na tym, że wasze zajęcia wiedzą o sobie zbyt wiele, co powoduje, że są one ściślej powiązane, co z kolei sprawia, że ​​są one bardziej kłopotliwe i trudniejsze do przetestowania.

Z mojego doświadczenia wynika, że ​​najczęstsze naruszenia Prawa Demeter są poglądami.Ponieważ starasz się skapitalizować nazwę, domyślam się, że tak właśnie jest. Soooo ...

Czy możesz użyć pomocnika widoku? Niestety, nie wiele dobrego na Rails, więc jest to pseudocodish:

def upcased_name(entity_with_name) 
    if entity_with_name != nil 
     entity_with_name.name.try(:upcase) 
    end 
end 

Następnie w widoku, wystarczy zadzwonić

<% upcased_name(person.pet) %> 

można przetestować upcased_pet_name wstrzykując różne wartości do viewhelper.

Teraz:

  • Państwa zdanie wie tylko, że ma dostęp do „osoba”, i dostęp do viewhelper nazwie upcased_name.
  • Twój model Person wie tylko, że ma on metodę Pet.
  • Twoja przeglądarka wie tylko, że może odbierać encję z metodą nazwy lub nil.

Bum! Twoje zajęcia wiedzą tylko o swoich znajomych i nic o przyjaciołach ich przyjaciół.

+0

Twój kod zawiera pewne problemy. Przede wszystkim nie wprowadziłbym go do pomocy, ponieważ nie jest to wyłącznie kwestia poglądu. Następnie <% = upcased_name (person.pet)%> zamiast <% upcased_name (person.pet)%> i co jeśli ta osoba jest zerowa? Następnie entity_with_name.name.try (: upcase) if entity_with_name.nil? zamiast sztywności, jeśli entity_with_name! = zero, a przynajmniej podejście delegata działa płynniej niż try thingi, co jest bardziej kosztowne i odwołuje się bardziej do wyrażenia prób i błędów. –