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

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

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

4 ответа

Решение

Если вы прокрутите ближе к концу

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

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

"Есть четыре способа выразить ситуацию в Smalltalk".

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

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

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

  • Если ваши условия типовые испытания

    anObject isKindOf: aClass
    

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

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

    anObject = anotherObject
    

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

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

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

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

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

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

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

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

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

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

или же

Самостоятельное действиеДействие Выполнение: действие Args

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

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

Другие вопросы по тегам