8

Mam temat do omówienia. Mam fragment kodu z 24 ifs/elifs. Operacja to moja własna klasa reprezentująca funkcjonalność podobną do Enum. Oto fragment kodu:Zbyt wiele instrukcji if

if operation == Operation.START: 
    strategy = strategy_objects.StartObject() 
elif operation == Operation.STOP: 
    strategy = strategy_objects.StopObject() 
elif operation == Operation.STATUS: 
    strategy = strategy_objects.StatusObject() 
(...) 

Mam obawy z punktu widzenia czytelności. Czy lepiej jest zmienić to na 24 klasy i użyć polimorfizmu? Nie jestem przekonany, że sprawi to, że mój kod będzie możliwy do utrzymania ... Z jednej strony, te wskazówki są całkiem jasne i nie powinno być trudno je śledzić, z drugiej strony jest ich zbyt wiele.

Moje pytanie jest dość ogólne, jednak piszę kod w pythonie, więc nie mogę używać konstrukcji takich jak przełącznik.

Co myślisz?

UPDATE:

Jedną ważną rzeczą jest to, że StartObject(), StopObject() i StatusObject() są konstruktorzy i chciałem przypisać obiekt do strategii odniesienia.

+0

Wyrażenie "case", zamiast tego? to lub mają tablicę odwołań do metody dynamicznie wywoływania, np. jakikolwiek pythonowy odpowiednik 'array_of_methods [operation]()' byłby ... –

+3

Marc, python nie ma instrukcji switch/case. https://www.python.org/dev/peps/pep-3103/ – Stiffo

Odpowiedz

12

Możliwe, że możesz użyć słownika. Słowniki referencje store, co oznacza funkcje są doskonale opłacalne w użyciu, tak:.

operationFuncs = { 
    Operation.START: strategy_objects.StartObject 
    Operation.STOP: strategy_objects.StopObject 
    Operation.STATUS: strategy_objects.StatusObject 
    (...)     
} 

Dobrze jest mieć operację domyślną na wszelki wypadek, więc po uruchomieniu go użyć try except i obsługi wyjątków (czyli równowartość swojej klauzuli else)

try: 
    strategy = operationFuncs[operation]() 
except KeyError: 
    strategy = strategy_objects.DefaultObject() 

Alternatywnie można użyć get metody słownika, który pozwala określić domyślne jeśli podasz klucz nie zostanie znaleziony.

strategy = operationFuncs.get(operation(), DefaultObject()) 

Pamiętaj, że nie wpisujesz nawiasów podczas zapisywania ich w słowniku, wystarczy użyć ich podczas wywoływania słownika. Wymaga to także, aby Operation.START było stabilne, ale tak powinno być, ponieważ opisałeś to jako klasę podobną do ENUM.

0

Jeżeli Operation.START itp są hashable, można użyć słownika z kluczy jako stanu i wartości jako funkcje zadzwonić, przykład -

d = {Operation.START: strategy_objects.StartObject , 
    Operation.STOP: strategy_objects.StopObject, 
    Operation.STATUS: strategy_objects.StatusObject} 

a następnie można zrobić tego słownika i wywołać funkcja, Przykład -

d[operation]() 
3

Odpowiednikiem Pythona do instrukcji switch jest użycie słownika. Zasadniczo możesz przechowywać klucze tak, jak robisz przypadki, a wartości są tym, co można by nazwać dla tego konkretnego przypadku.Ponieważ funkcje są obiekty w Pythonie można przechowywać je jako wartości słownika:

operation_dispatcher = { 
    Operation.START: strategy_objects.StartObject, 
    Operation.STOP: strategy_objects.StopObject, 
} 

które mogą być następnie wykorzystane w następujący sposób:

try: 
    strategy = operation_dispatcher[operation] #fetch the strategy 
except KeyError: 
    strategy = default #this deals with the else-case (if you have one) 
strategy() #call if needed 

lub więcej zwięźle:

strategy = operation_dispatcher.get(operation, default) 
strategy() #call if needed 

Może to potencjalnie skalowalne o wiele lepiej niż posiadające bałagan instrukcji if-else. Zauważ, że jeśli nie masz innego przypadku, z którym możesz sobie poradzić, możesz po prostu użyć słownika bezpośrednio z operation_dispatcher[operation].

-1

Oto bastardized switch/case odbywa się za pomocą słowników:

Na przykład:

# define the function blocks 
def start(): 
    strategy = strategy_objects.StartObject() 

def stop(): 
    strategy = strategy_objects.StopObject() 

def status(): 
    strategy = strategy_objects.StatusObject() 

# map the inputs to the function blocks 
options = {"start" : start, 
      "stop" : stop, 
      "status" : status, 

} 

Następnie odpowiednikiem bloku przełącznik jest wywoływana:

options["string"]() 
+1

Ale "strategia" będzie lokalna dla każdej z tych małych funkcji i nie będzie widoczna w zakresie, w którym wykonasz 'options [" ciąg znaków "]()'. – Kevin

1

możesz używać introspekcji z getattr:

strategy = getattr(strategy_objects, "%sObject" % operation.capitalize())() 

Powiedzmy, że operacja jest "STATUS", zostanie wpisana jako "Status", a następnie dodana do "Obiektu", podając "StatusObject". Metoda StatusObject będzie następnie wywoływana na strategy_objects, ulegając awarii, jeśli ten atrybut nie istnieje lub nie można go wywołać. :) (tj. Dodaj obsługę błędów.)

Rozwiązanie słownikowe jest prawdopodobnie bardziej elastyczne.

+0

Sprawdzanie klasy za pomocą hasattr (...) byłoby odpowiednikiem KeyError w rozwiązaniu słownikowym. –

2

Możesz spróbować czegoś takiego jak this.

Na przykład:

def chooseStrategy(op): 
    return { 
     Operation.START: strategy_objects.StartObject 
     Operation.STOP: strategy_objects.StopObject 
    }.get(op, strategy_objects.DefaultValue) 

nazwać to jak ten

strategy = chooseStrategy(operation)() 

Metoda ta ma tę zaletę, zapewniając domyślną wartość (jak finał else). Oczywiście, jeśli potrzebujesz tylko tej logiki decyzyjnej w jednym miejscu kodu, zawsze możesz użyć strategii = dictionary.get (op, domyślnie) bez funkcji.

+0

Brakuje połączenia. 'strategy = chooseStrategy (operation)()' – Brobin

+0

Dzięki! Tęskniłem za tym, że nazwał wybraną strategię. Zmodyfikowałem mój post. – weirdev

Powiązane problemy