понедельник, 15 мая 2017 г.

if-then-else

В большинстве случаев (возможно, даже во всех) if-then-else может и должен быть заменен декоратором или просто другим объектом. 


 
Взгляните на класс DyTalk от yegor256 / rultor и его метод modify (). В двух словах, он не позволяет вам сохранять любые данные в DynamoDB, если не было изменений в XML-документе. Это реальный случай, и его необходимо рассмотреть. Первоначальный способ реализации неправильный:

Класс DyTalk реализует Talk {
  
Void modify (Collection <Directive> dirs) {
    
If (! Dirs.isEmpty ()) {
      
// Применить модификацию
      
// и сохраним новый XML-документ
      
// в таблицу DynamoDB.
    
}
  
}}


Конструкция if-then-else на самом деле не нужна объекту. Вместо этого должен быть декоратор, который будет выглядеть следующим образом:

Класс QuickTalk реализует Talk {
 
private final Talk origin;
  
Void modify (Collection <Directive> dirs) {
    
If (! Dirs.isEmpty ()) {
      
this.origin.modify () каталоги;
    
}
  
}}


Конструкция if-then-else на самом деле не нужна объекту Вместо этого должен быть декоратор, который будет выглядеть следующим образом:Теперь, если и когда нам нужно, чтобы наша речь была более умной в ситуациях, когда список директив пуст, мы украшаем его QuickTalk. Преимущества очевидны: класс DyTalk меньше и поэтому более сплочен 

Если возможно преобразовать if-then-else в декоратор, это должно быть сделано 

Но вопрос больше, чем просто этот случай. Будут ли  исключения? Можем ли мы сказать, что каждое разветвление плохо и должно быть перенесено из класса? Что можно сказать о разветвлении, которое происходит внутри метода и не может быть преобразовано в декоратор? 

Предлагаю простое правило: если возможно преобразовать if-then-else forking в декоратор, это должно быть сделано. Если это не сделано, это дурно пахнущий код.



Отзывы и комментарии:

Это не имеет никакого смысла! Это приведет к взрыву сложности кода, так как десятки классов, не служащие никакой другой цели, кроме ухода от условных операторов, создаются с еще более запутанными именами...

Напротив, заявленное решение будет менее многословным и более понятным, особенно с ростом сложность кода. Можно быстро превратить сильно дублированные и беспорядочные IF / THEN / ELSE в маленькие прозрачные объекты (хотя это потребует некоторого творчества и времени, чтобы придумать все их имена). Так лучше, потому что это намного более ремонтопригодно.

Таким образом, все библиотеки должны предоставлять также классы проверки...

Правильно, все библиотеки должны предоставлять классы проверки. И некоторые другие классы / декораторы тоже.

Хотя подобные решения (код «только из объектов») изящны, но стоят ли они лишнего «материала», чтобы избегать простой инструкции if? Недавно я заменил цикл for связанным списком, потому что он имел смысл для объекта, который я моделировал. Я должен признать, что у меня есть определенное удовлетворение в том, что я могу заменить процедурный код на чистую структуру объекта. Мои коллеги были ошеломлены и встревожены тем, что я реализовал такое усложнение, когда простого цикла было бы достаточно.

Это усложнение только потому, что наши языки не созданы, чтобы быть действительно объектно-ориентированными. Они дают нам достаточно «цикльных» операторов, но недостаточно объектов, которые бы делали то же самое. Таким образом, нужно винить язык, а не ООП.

Действительно ли можно избавиться от оператора If-else? Теперь ваш код ясен, но сложность If-else перемещена в код конечного пользователя. Пользователь библиотеки должен определить, является ли ее коллекция пустой или нет. Таким образом, общее число операторов If-else в мире даже увеличилось.

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

С помощью паттерна-посетитель можно использовать метод, без инструкции if, или даже с фабрикой / картой.

Проблема с этим подходом заключается в том, что он делает класс ExampleUsage изменяемым, что не очень хорошо .. но он иллюстрирует, как вы могли бы разработать алгоритм, который позволяет избежать использования оператора if ...

Проблема с операторами if заключается в том, что программисту очень легко написать больше строк кода, а затем просто построить реализацию для некоторой абстрактной переменной. Я считаю, что если вам нужно иметь оператор if, то просто вернуть или назначить реализацию абстрактной переменной. Нет, если / else блок должен быть более одной строки.

Я не обязательно выступаю за такой подход, но мне он нравится. Используя карту / словарь, в качестве фабрики, вам никогда не придется фактически писать оператор if в своем коде, и поэтому вы вынуждаете только предоставлять реализации в тех областях, где выполняются вилки исполнения кода.

Хорошее упражнение! :) Иногда я заменяю case картами.

Читая название статьи, я был на 100% уверен, что я увижу хороший пример того, как определить хорошие полиморфные отношения между объектом или использование шаблона Visitor и объяснение метода двойной отправки. Но я никак не ожидал увидеть другую статью о декораторах.

Вы избавились от «if-then-else»? Нет! А что сделали? Делегировали в декоратор ... Таким образом только добавилл сложность. Также интересно, как вы моделируете сложные if-then-else, используя этот подход? Как насчет switch-case с 10 заявлениями?

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

Например, я не понимаю этого: почему я должен использовать декоратор, если у нас есть более подходящая модель для этого? (Я думаю о шаблоне Criteria / Specification)? Например:

public ValidatedXmlStore implements XmlStore {
  private final XmlStore store;
  private final Predicate<xml> validation;

  public ValidatedXmlStore(...) { ... }

  public boolean save(Xml xml) {
    return validation.test(xml) ? store.save(xml) : false;
  }
}

Предикат - замечательный интерфейс (по крайней мере, в Java8) для реализации шаблона спецификации, вы можете создать свою проверку, создавая различные объекты Predicate и составляя их.

Ваше решение хорошо, когда: 1) у вас много декораторов; 2) вы не можете изменить исходный код; 3) исходный исходный код уже достаточно сложен

В других случаях ваше решение просто создает ненужные усложнения.

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

Не только требуется гораздо больше времени, чтобы добавить простую проверку с простым if, но декораторы выглядят менее читабельно - имя декоратора скорее вводит в заблуждение (или, по крайней мере, не информативно). Оно должно быть что-то вроде 'IgnoreEmptyDirectivesTalk'


То, что вы предлагаете, - это дурнопахнущий код. тратится впустую время.

Напротив. Для меня запах IFThenElse - это когда вы запрашиваете объект для информации, и исходя из полученного результата, вы предпринимаете соответствующее действие.

Я не знаю, что делает код в конечном счете, но здесь объект Dy / QuickTalk должен знать, как вести себя в зависимости от состояния коллекции, и каково условие для работы. Поэтому основной вопрос: почему мы не позволяем коллекции выбирать, если она нуждается в обновлении? Мы просто рассказываем коллекцию, что мы ожидаем, и коллекция сама позаботится о том, делать это или нет. Поэтому, если бы я использовал декораторы или наследование, они были бы в объекте Collection.

Я ожидал увидеть подробное объяснение того, как использовать полиморфизм, чтобы избежать if-then-else, но декораторы? Еще раз?
Очень разочаровывает и пример с проверкой на null, что объявлялось автором злом ранее!.

У вас просто есть «if». И вы не избавляетесь от этого «if», вы просто перемещаете его. Как это поможет?

Это больше касается обязанностей этого объекта ...    Да, именно, речь идет о разложении ответственности. Это должно быть сделано между двумя объектами. И я предлагаю всегда делать это, когда мы видим, if-then-else.

Думаю, Decorator для оператора IF увеличит количество строк кода (LOC). Похоже, что одна строка IF-оператор займет дополнительно лишних 10 и более строк в этом подходе с Декоратором! Кажется, эт не хороший выборю

Да, код с if меньше, но выгода от использования объектов это компенсирует.

Я думаю, что лучше иметь два небольших класса, чем один большой. В общем случае, если мы можем разбить класс на более мелкие классы, мы должны это сделать.

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

Как всегда говорил мой друг: «Спагетти код в ООП становится кодом лазаньи».

Разделить код на классы, которые не будут повторно использоваться или расширены позже, не стоит усилий ИМО.

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

Я очень согласен с этой статьей. Я просто хотел ввести правило, которое я буду развивать в будущем: каждый IF-THEN-ELSE является потенциальным дурнопахнущим кодом, если используется вместе с другим кодом.
  
Каждый IF-THEN-ELSE может быть заменен правильным полиморфизмом, в то время как здесь ваше предложение имеет так много недостатков и очевидных предостережений. Так что нужно быть очень осторожным. Например, каждый раз, когда вы предлагаете использовать декоратор и то, как вы предлагаете использовать их, скорее всего, нарушает принцип замены.
 

 

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

Отправить комментарий