Сделайте это, не используя "if" | if (s == "value1" ) {...} else if (s == "value2" ) {...}

Согласно анти-кампания, лучше не использовать ifs в нашем коде. Может ли кто-нибудь сказать мне, можно ли избавиться от if в этом фрагменте кода? (переключатель также не является опцией, Задача состоит в том, чтобы удалить условную логику, а не заменять ifs аналогичными языковыми конструкциями)

if(s == "foo")
{
    Writeln("some logic here");
}
else if(s == "bar")
{
    Writeln("something else here");
}
else if(s == "raboof")
{
    Writeln("of course I need more than just Writeln");
}

(язык: Java или С#)

Ответ 1

Используйте шаблон стратегии .

В терминах Java:

public interface Strategy {
    void execute();
}

public class SomeStrategy implements Strategy {
    public void execute() {
        System.out.println("Some logic.");
    }
}

который вы используете следующим образом:

Map<String, Strategy> strategies = new HashMap<String, Strategy>();
strategies.put("strategyName1", new SomeStrategy1());
strategies.put("strategyName2", new SomeStrategy2());
strategies.put("strategyName3", new SomeStrategy3());

// ...

strategies.get(s).execute();

Ответ 2

Здесь один путь...:)

delegate void DoStuff();

...

IDictionary<string, DoStuff> dict = new Dictionary<string, DoStuff>();
dict["foo"] = delegate { Console.WriteLine("some logic here"); };
dict["bar"] = delegate { Console.WriteLine("something else here"); };
dict["raboof"] = delegate { Console.WriteLine("of course I need more than just Writeln"); };
dict["foo"]();

Ответ 3

Составить ассоциативную структуру данных. Map<String, String> в Java, IDictionary<string, string> в С#. Инициализируйте его в начале времени, а затем...

Ответ 4

писать классы с виртуальными методами, которые получены из вашего абстрактного базового класса SomeThingWriter.

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

abstract class MyBaseClass
{
     public abstract void writeSomething();
}

class DerivedClass1 : MyBaseClass
{
    public override void writeSomething()
    {
        Writeln("something else here  1");
    }
}

class DerivedClass2 : MyBaseClass
{
    public override void writeSomething()
    {
        Writeln("something else here  2");
    }
}

чем просто называть

MyBaseClass c = new DeriveClass1();
c.writeSomething();
c = new DerivedClass2();
c.writeSomething();

Ответ 5

Взглянув на кампанию, она очень плохо объяснила. Там нет ничего плохого в ifs, но в некоторых случаях они могут указать, что вы не используете OOP в полном объеме.

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

Вы использовали бы более интеллектуальный объект, а не как строку:

interface I {
  public String getName();
  public void doSomething();
}

class A implements I {
  public String getName() { return "one"; }
  public void doSomething() { ...; }
}

class B implements I {
  public String getName() { return "two"; }
  public void doSomething() { ...; }
}

Затем вы можете заменить ifs на:

I obj = ...get an A or B from somewhere...;
obj.doSomething();

Ответ 6

Java

Используйте перечисление, которое реализует определенный метод.

enum MyEnum{

    foo{
        public void mymethod(String param1, String param2){
            //dostuff...
        }
    },

    bar{
        public void mymethod(String param1, String param2){
            //dostuff...
        }
    };

    public abstract void mymethod(String param1, String param2);
}

Затем в вашем классе:

MyEnum.valueOf(mystring).mymethod(param1, param2);

Ответ 7

В некоторых случаях может быть законным избегать структуры if

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

В то время как примеры, предоставленные для избежания структуры if, являются действительными альтернативами, вы должны спросить себя об этом:

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

Ответ 8

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

  • Спросите себя, может ли кампания Anti IF исключить логику в приложениях?!
  • Идеи могут иметь хорошее применение в одной ситуации и глупо в другой. Будьте разумными.
  • Возможно, многократное использование IF может обременять читателя кодом. но это любая причина для устранения if из вашего кода, более того, это почти невозможно.
  • Кстати, в любом месте руководства по проектированию MS не используется , если (например, сделано, например, для оператора goto использование witch не рекомендуется)...

С#

    switch (myStringVar)
    {
        case "one": doSomething();  break;
        case "two": doSomething(); break;
        case "three": doSomething(); break;
        default: doSomething(); break;
    }

Наконец, он уменьшает этот код до , если s... так что только для удобства чтения лучше, а не для производительности.

На самом деле, если Microsoft полагает, что переключатель (в С#) лучше заменить на if - OK, я буду использовать (в конкретной ситуации, о которой вы описали) переключатель .

Кстати, кажется, что кампания отвечает на ваш вопрос очень ясно в этом примере

Ответ 9

Я хотел бы отметить, что до сих пор каждый ответ на этот вопрос с примером кода имеет решение, которое намного сложнее исходного кода и, вероятно, намного медленнее.

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

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

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

Ответ 10

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

В Fowler Рефакторинг, он обсуждает Заменить условный с Полиморфизм. Это то, что я считаю полезным для замены операторов if/switch (при необходимости).

Ответ 11

Я не думаю, что вы делаете справедливое сравнение здесь.

С учетом этого кампания Anti-if предназначена для практического применения более совершенного подхода к дизайну.

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

И почему именно это?

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

Ответ 13

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

public class FooOrBar {
private Map<String, Method> methodMap = new HashMap<String, Method>();

public FooOrBar() {
    try {
        methodMap.put("foo", this.getClass().getMethod("doFoo", new Class[0]));
        methodMap.put("bar", this.getClass().getMethod("doBar", new Class[0]));
    } catch (NoSuchMethodException n) {
        throw new RuntimeException(n);
    }
}

public void doSomething(String str) {
    Method m = methodMap.get(str);
    try {
        m.invoke(this, null);
    } catch (Exception n) {
        throw new RuntimeException(n);
    }

}

public void doFoo() {
    System.out.println("foo");
}

public void doBar() {
    System.out.println("bar");
}

public static void main(String[] args) {
    FooOrBar fb = new FooOrBar();
    fb.doSomething("foo");

}

}

Ответ 14

Нарушить тернарный оператор, по крайней мере, в С#:

Action result = 
            s == "bar" ? (Action)(() => { Console.WriteLine("bar"); }): 
            s == "foo" ? (Action)(() => { Console.WriteLine("foo"); }) :
                         (Action)(() => { Console.WriteLine(); });

Собственно, я забираю это обратно... никогда НИКОГДА не делай этого. Используйте переключатель.

Ответ 15

Я читаю http://www.antiifcampaign.com/articles/the-simplest-anti-if-code.html, и я думаю, что лекарство хуже, чем болезнь. Многое, намного хуже. Вы должны инвестировать в некоторые тяжелые машины OO для решения возможной (невероятной?) Будущей проблемы.

Ответ 16

Немного поздно для вечеринки, но, сочетая словарные словаря С# от MRFerocius и cletus, дает после выполнения bmargulies ответа:

private Dictionary<string,Action> data = new Dictionary<string, Action> {
    {"foo", () => Console.WriteLine("Some logic here")},
    {"bar", () => Console.WriteLine("something else here")},
    {"raboof", () => Console.WriteLine("of course I need more than just WriteLine")},
}

public static void main(String[] args) {
    data["foo"]();
}
  • Если ключ не существует в словаре, его использование в индексаторе вызовет исключение.
  • Можно выполнить несколько действий:

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

      {"foobar", () => { data["foo"](); data["bar"](); }

    • Поскольку Action - тип делегата, к одному экземпляру делегата могут быть присоединены несколько методов, и экземпляр делегирования задается как значение; они будут вызываться последовательно при вызове делегата:

      public static void main(String[] args) { data["foobar"] = data["foo"] + data["bar"]; //This will invoke first data["foo"] then data["bar"] data["foobar"](); }

      Для методов, не связанных через словарь, это также можно сделать в инициализаторе коллекции:

      {"foobar", (Action)method1 + method2}

Ответ 17

Вот мой. Использование LINQ и Factory Выкройка: D

class FactoryString
    {
    static FactoryString()
    {
    private static Dictionary<string, string> dictionary = new Dictionary<string, string> 
    { 
        {"foo", "some logic here"},
        {"bar", "something else here"},
        {"raboof", "of course I need more than just Writeln"},
    };
}

public static string getString(string s)
{
    return dictionary.Single(x => x.Key.Equals(s)).Value;
}

}

static void main()
{
  Console.WriteLine(FactoryString.getString("foo"));
}

Ответ 18

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

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

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

Следует подчеркнуть, что довольно легко написать unit test, который покрывает 100% этого кода. Также стоит подчеркнуть, что сложность этого кода линейно масштабируется с количеством кодов назначения, предикатов и обновлений, которые он поддерживает; если используются случаи или если используются выражения, он будет масштабироваться экспоненциально.

    /// <summary>
    /// Update a sentence status to Completed [401110]
    /// </summary>
    /// <param name="senRow"></param>
    /// <param name="eventDate"></param>
    private static void CompleteSentence(DataRow senRow, DateTime eventDate)
    {
        senRow.SetField("SenStatus", "401110");
        senRow.SetField("SenStatusDate", eventDate);
    }

    /// <summary>
    /// Update a sentence status to Terminated [401120]
    /// </summary>
    /// <param name="senRow"></param>
    /// <param name="eventDate"></param>
    private static void TerminateSentence(DataRow senRow, DateTime eventDate)
    {
        senRow.SetField("SenStatus", "401120");
        senRow.SetField("SenStatusDate", eventDate);
    }

    /// <summary>
    /// Returns true if a sentence is a DEJ sentence.
    /// </summary>
    /// <param name="senRow"></param>
    /// <returns></returns>
    private static bool DEJSentence(DataRow senRow)
    {
        return Api.ParseCode(senRow.Field<string>("SenType")) == "431320";
    }


    /// <summary>
    /// Returns true if a sentence is a Diversion sentence.
    /// </summary>
    /// <param name="senRow"></param>
    /// <returns></returns>
    private static bool DiversionSentence(DataRow senRow)
    {
        return Api.ParseCode(senRow.Field<string>("SenType")).StartsWith("43");
    }

    /// <summary>
    /// These are predicates that test a sentence row to see if it should be updated
    /// if it lives under a charge disposed with the specified disposition type.
    /// 
    /// For instance, if the PDDispositionCode is 413320, any DEJ sentence under the
    /// charge should be updated.
    /// </summary>
    private static readonly Dictionary<string, Func<DataRow, bool>> PDSentenceTests = 
        new Dictionary<string, Func<DataRow, bool>>
    {
        {"411610", DiversionSentence},  // diversion successful
        {"413320", DEJSentence},        // DEJ successful
        {"442110", DiversionSentence},  // diversion unsuccessful
        {"442111", DiversionSentence},  // diversion unsuccessful
        {"442112", DiversionSentence},  // diversion unsuccessful
        {"442120", DEJSentence}         // DEJ unsuccessful
    };

    /// <summary>
    /// These are the update actions that are applied to the sentence rows which pass the
    /// sentence test for the specified disposition type.
    /// 
    /// For instance, if the PDDispositionCode is 442110, sentences that pass the sentence
    /// test should be terminated.
    /// </summary>
    private static readonly Dictionary<string, Action<DataRow, DateTime>> PDSentenceUpdates = 
        new Dictionary<string, Action<DataRow, DateTime>>
    {
        {"411610", CompleteSentence},   // diversion successful (completed)
        {"413320", CompleteSentence},   // DEJ successful (completed)
        {"442110", TerminateSentence},  // diversion unsuccessful (terminated)
        {"442111", TerminateSentence},  // diversion unsuccessful (terminated)
        {"442112", TerminateSentence},  // diversion unsuccessful (terminated)
        {"442120", TerminateSentence}   // DEJ unsuccessful (terminated)
    };

    private void PDUpdateSentencesFromNewDisposition()
    {
        foreach (DataRow chargeRow in PDChargeRows
            .Where(x => PDSentenceTests.ContainsKey(x.Field<string>("PDDispositionCode"))))
        {
            string disp = chargeRow.Field<string>("PDDispositionCode");
            foreach (DataRow s in CHGRows[chargeRow]
                .ChildRows("CAS-SUBCRM-CHG-SEN")
                .Where(x => PDSentenceTests[disp](x)))
            {
                PDSentenceUpdates[disp](s, EventDate);
            }
        }
    }