Рефакторинг цепочек if в Smalltalk без взрыва класса

#if-statement #refactoring #smalltalk

#оператор if #рефакторинг #smalltalk

Вопрос:

Поскольку Smalltalk не рекомендует использовать caseOf: , какие существуют альтернативы для реализации следующей ситуации без взрыва класса?

 self condition1
        ifTrue: [ self actionForCondition1 ]
        ifFalse: [ 
            self condition2
                ifTrue: [ self actionForCondition2 ]
                ifFalse: [ 
                    self condition3
                        ifTrue: [ self actionForCondition3 ]
                        ifFalse: [ ....  ] ] ]
  

Ответ №1:

Зависит от того, как именно выглядят ваши условия?

  • Если ваши условия являются тестами типа

     anObject isKindOf: aClass
      

    вместо этого вы можете отправлять в классе, это означает, что вы вызываете метод action в anObject .

  • Если ваши условия являются тестами на равенство

     anObject = anotherObject
      

    вы можете использовать словарь с объектом в качестве ключа и действием в качестве закрытия блока.

  • В качестве последнего восстановления, и если больше ничего не помогает, вы можете захотеть переписать предлагаемый код, чтобы избежать ненужной вложенности:

     self condition1
        ifTrue: [ ^ self actionForCondition1 ].
    self condition2
        ifTrue: [ ^ self actionForCondition2 ].
    self condition3
        ifTrue: [ ^ self actionForCondition3 ].
    ...
      

Комментарии:

1. 1. Это последнее средство функционально идентично «не рекомендуется» #caseOf: .

Ответ №2:

Если вы прокрутите до конца

http://www.desk.org:8080/CampSmalltalk/control flow

вы найдете предложение

«Есть четыре способа выразить оператор case в Smalltalk».

далее следуют ссылки на примеры.

Текст находится в середине немного более длинной серии страниц и содержит случайные ссылки на гипотетического преподавателя и студентов в курсе Smalltalk в целях иллюстрации; вы можете проигнорировать это для целей вашего вопроса)

Ответ №3:

Я думаю, в тот момент, когда вам нужно написать этот код, я бы спросил себя, почему я должен писать так много условий, чтобы продолжить выполнение одного шага в моем алгоритме. Может быть, пришло время подумать, что не так с model? Особенно, если учесть, что семантика поиска сообщений на самом деле является оператором case:

 selector = selector1 ifTrue: [ invoke method1 ] 
  ifFalse: [ selector= selector2 ifTrue: [ invoke method2 ] 
   ifFalse: [...] ]]].
  

Итак, вы должны попытаться превратить это в свое преимущество — используйте оператор switch виртуальной машины вместо написания собственного.

Применяя простой принцип: не спрашивать (object IsSomething ifTrue:[ self doSomething ]), а сообщать (object doSomething), вы можете избежать большого количества ветвей в коде. Конечно, иногда это неприменимо и сильно зависит от ситуации, но я часто предпочитаю иметь дополнительную отправку сообщений, а не другую ветвь в коде.

Ответ №4:

Вам следует взглянуть на двойную отправку. В зависимости от того, как реализованы тесты и действия, вы можете использовать двойную отправку с большим преимуществом.

В конечном итоге вы хотите получить код, который выглядит следующим образом:

выполнение действия с самообусловлением.

или

выполнение самодостаточного действия: actionArgs

Метод #conditionAction должен был бы возвращать уникальный экземпляр объекта для каждого уникального условия (без использования самих операторов case :).

Вы бы не хотели усугублять проблему, создавая классы только для того, чтобы избежать «операторов case», но в реальном мире у вас уже может быть несколько уникальных классов, которые вы можете использовать.