Подтвердить что ты не робот

Анти-кампания

Недавно я столкнулся с очень интересным сайтом, который выражает очень интересную идею - кампанию anti-if. Вы можете увидеть это здесь, в www.antiifcampaign.com. Я должен согласиться с тем, что сложные вложенные утверждения IF являются абсолютной болью в тылу. В настоящее время я нахожусь в проекте, который до недавнего времени имел некоторые сумасшедшие вложенные IFs, которые прокручивались вправо достаточно. Мы вылечили наши проблемы двумя способами: мы использовали Windows Workflow Foundation для решения проблем маршрутизации (или рабочего процесса). И мы находимся в процессе реализации всех наших бизнес-правил с использованием правил ILOG для .NET(недавно приобретенных IBM!!). Это по большей части вылечило наши вложенные боли ЕС... но я нахожу себя в недоумении, сколько людей излечивают свои боли так, как предлагают хорошие люди в AntiIfCampaign (см. пример здесь), создавая множество сумм абстрактных классов для представления данного сценария, который первоначально был покрыт вложенным IF. Интересно, может ли другой способ устранить эту сложность, может также заключаться в использовании контейнера IoC, такого как StructureMap, для перемещения и из разных бит функциональности. В любом случае...

Вопрос: Учитывая сценарий, когда у меня есть вложенный сложный оператор IF или SWITCH, который используется для оценки определенного типа вещи (например, оценка Enum), чтобы определить, как я хочу обрабатывать обработку этой вещи по типу перечисления - каковы некоторые способы сделать одну и ту же форму обработки без использования иерархической структуры IF или SWITCH?

public enum WidgetTypes
{
    Type1,
    Type2,
    Type3,
    Type4
}

...

WidgetTypes _myType = WidgetTypes.Type1;

...

switch(_myType)
{
    case WidgetTypes.Type1:
        //do something
        break;

    case WidgetTypes.Type2:
        //do something
        break;

    //etc...
}
4b9b3361

Ответ 1

Проблема не в выражении 'if', это программисты, которые пишут плохой код.

EDIT: Также, как указывали другие, вы должны использовать полиморфизм (если он доступен), когда вы используете операторы if для проверки типа объекта, но если сами по себе являются очень полезными и фундаментальными конструкциями.

Ответ 2

В Java легко использовать перечисления в качестве полиморфных агентов анти-if.

public class AntiIf {
  public enum WidgetTypes {
    Type1 {
      public void doSomething() {
        //...
      }},
    Type2 {
      public void doSomething() {
        //...
      }},
    Type3 {
      public void doSomething() {
        //...
      }},
    Type4 {
      public void doSomething() {
        //...
      }};

    public abstract void doSomething();
  }

  WidgetTypes _myType; // set by someone to one of the types.

  public void someFunction() {
    //...
    _myType.doSomething();
    //...
  }
}

Ответ 3

Быть анти-если глупо.

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

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

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

Быть анти-если глупо.

Ответ 5

Операторы switch часто могут быть заменены полиморфизмом:

abstract class WidgetBase {
  public abstract void DoSomething();
}

class Widget1 : WidgetBase {
  public override void DoSomething() {
    // Do something ...
  }
}

class Widget2 : WidgetBase {
  public override void DoSomething() {
    // Do something ...
  }
}

...

Widget widget = new Widget1(); // Use a factory pattern instead.
widget.DoSomething();

Ответ 6

Вы можете использовать Command Pattern для выполнения команд на основе некоторого значения или ввода.

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

var commands = new Dictionary<WidgetTypes, Action>()
    {
        { WidgetTypes.Type1, () => Console.WriteLine("Type 1") },
        { WidgetTypes.Type2, () => Console.WriteLine("Type 2") },
        { WidgetTypes.Type3, () => Console.WriteLine("Type 3") },
        { WidgetTypes.Type4, () => Console.WriteLine("Type 4") }
    };

commands[WidgetTypes.Type1]();

public interface ICommandHandler
{
    void HandleCommand();
}

public class Command1Handler : ICommandHandler
{
    public void HandleCommand()
    {
        Console.WriteLine("Type 1");
    }
}

// ...

var commands = new Dictionary<WidgetTypes, ICommandHandler>()
    {
        { WidgetTypes.Type1, new Command1Handler() },
        { WidgetTypes.Type2, new Command2Handler() },
        { WidgetTypes.Type3, new Command3Handler() },
        { WidgetTypes.Type4, new Command4Handler() }
    };

commands[WidgetTypes.Type1].HandleCommand();

И метод бонусного отражения для вызова метода для текущего объекта с тем же именем:

public void Type1()
{
    Console.WriteLine("Type 1");
}

//...

var type = WidgetTypes.Type2;
typeof(MyClass).GetMethod(type.ToString()).Invoke(this, new object[] { });

Примечание: на самом деле этого не делают

Ответ 7

... Что?

Я не могу себе представить, как сделать что-то подобное без какого-либо сравнения. На всех.

Противоугонная кампания, которую я могу себе представить, но если мне кажется, что мне нужна чертова структура управления потоком.

Ответ 8

Не беспокойтесь о простой инструкции if или инструкции прямого преобразования.

Побеспокоитесь об этом:

  • Ужасно вложенные операторы if (анти-шаблон Arrowhead). Эти вещи невозможно прочитать и отладить.
  • Если (или случай) с строками кода баглиона в них. "Хорошо, мы делаем это и это... в этом случае, и это, и то... в этом случае. Также очень трудно читать. Почти невозможно проверить или отладить.
  • Супер сложные условия: если (чаша и кухонный гарнитур или ванная комната...). Что вы снова тестируете? Оберните это методом - в этом случае: CanHoldWater().

Ответ 9

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

_myDictionary[ _myType ]( param1, ... );

Ответ 10

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

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

  • Откорректируйте бизнес-логику без перекомпиляции.
  • В экземплярах базы данных может использоваться один и тот же код, но с другой логикой.

Я не говорю о прямом встраивании исходного кода в поле varchar() базы данных и компиляции его на лету.

Но скажем, у вас есть список полей ввода, и для каждого из них вам нужно выполнить (a) некоторый RegEx для очистки значения и (b) один или несколько тестов для проверки результата.

В мире только для кода у вас будет подкласс для каждого типа поля, каждый из которых реализует, скажем, собственные методы ScrubValue() и Validate().

В шаблоне на основе таблицы один класс Field будет попадать в базу данных (естественно, кэшируется) на основе свойства FieldType и получить набор строк поиска/замены RegEx и список параметров проверки достоверности (минимальные и максимальная длина, шаблоны регулярных выражений для соответствия и т.д.).

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

Ответ 11

Вау... это для реального?

Вы можете попытаться поймать абсолютно любое исключение, если вы спуститесь по неправильному пути выполнения, пока не нажмете путь выполнения, который НЕ ломается, но это FAR хуже, чем если.

Вы можете использовать шаблон команды или карту, когда объектная ориентация и полиморфизм естественны, но что, если вы имеете дело с результатами ввода пользователя, которые являются сложными и НЕ сами по себе объектами?

Ответ 12

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

Ответ 13

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

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

Я не уверен, что полиморфизм - правильный ответ для этого. У кого-нибудь есть лучшие идеи?

Ответ 14

Операторы case отлично работают для организации вложенных ifs.

Ответ 15

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

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

Я придумал шаблонную структуру кода, которая позволила мне "объявить" правильные функции (около 30 или около того) вместо того, чтобы хрустнуть их в одну процедуру диспетчеризации.

Это позволило нам очень быстро добавить новые случаи конфигурационных параметров и попросту "скопировать" уравнения из математического обзора. На самом деле это действительно победа.