不使用“if”|来执行此操作 if(s ==“value1”){…} else if(s ==“value2”){…}
根据反if运动 ,最好不要在我们的代码中使用ifs。 任何人都可以告诉我是否有可能摆脱这段代码中的if? (switch也不是一个选项, 重点是删除条件逻辑,而不是用类似的语言结构替换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或C#)
利用战略模式 。
用Java术语:
public interface Strategy { void execute(); } public class SomeStrategy implements Strategy { public void execute() { System.out.println("Some logic."); } }
您使用如下:
Map strategies = new HashMap(); strategies.put("strategyName1", new SomeStrategy1()); strategies.put("strategyName2", new SomeStrategy2()); strategies.put("strategyName3", new SomeStrategy3()); // ... strategies.get(s).execute();
这是一种方式…… 🙂
delegate void DoStuff(); ... IDictionary dict = new Dictionary(); 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"]();
建立关联数据结构。 在Java中Map
,在C#中Map
IDictionary
。 在开始时初始化它,然后……
看看这个广告系列,解释得非常糟糕。 ifs没有任何问题,但在某些情况下,它们可以表明你没有充分利用OOP。
该活动试图推广的是增加多态性的使用,以便将调用代码与它正在查看的对象类型分离。
你会使用一些更聪明的对象,而不是s是一个字符串:
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();
使用从抽象基类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();
在某些情况下,避免if结构可能是合法的
在其他人,它只是普通的白痴,试图避免如果。
虽然为避免if结构而提供的示例是有效的替代方案,但您应该问自己:
为什么我要使我的代码不必要复杂以避免简单的if结构? 如果唯一的原因是你必须因为反竞选活动那么它的坏理由
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); }
然后在你的class上:
MyEnum.valueOf(mystring).mymethod(param1, param2);
首先,在阅读这些“反”运动时要非常注意 。
- 问问自己, 反IF活动是否会消除应用程序中的逻辑 ?!
- 这些想法可以在一种情况下有一个很好的应用,在另一种情况下也可以有愚蠢。 合理。
- IF的 多次使用可能会妨碍代码的读者。 但这是从你的代码中消除if的 任何理由,更多的是,这几乎是不可能的。
- 顺便说一下MS设计指南中的任何地方都表示不要使用if (就像完成时一样,例如goto语句不建议使用女巫)…
C#
switch (myStringVar) { case "one": doSomething(); break; case "two": doSomething(); break; case "three": doSomething(); break; default: doSomething(); break; }
最后,它将此代码缩减为if …所以,只是为了可读性更好,而不是性能。
实际上,如果微软认为交换机(在c#中)最好用if替换 – 好的,我将使用(在您描述的具体情况下) 交换机 。
顺便说一句, 在这个例子中 ,该活动似乎非常清楚地回应了您的问题
我想指出,到目前为止,使用代码示例对此问题的每个答案都有一个比原始代码复杂得多的解决方案,并且可能要慢得多。
这是在完全错误的上下文中执行优化的经典案例。 在某些情况下,通过正确使用OO,代码将变得更加清晰,例如消除长链类型检查。 但是,只是删除所有if语句只是为了删除它们只会混淆你的代码。
if语句(条件跳转)仍然会发生,无论是在你的代码还是解释器中。 保持它们在词汇上靠近具有许多可读性和维护优势,这些优点通过过度使用OO而丢失。 必须在本地逻辑与远程逻辑之间取得平衡,但它绝不应该渗透到混淆中。
对于手头的问题,避免if
的最清晰的构造可能是包含匿名函数的哈希表/关联数组,对于少量的键,它实际上只是一个慢的switch语句。
你给我的例子我不会改变(虽然我猜你意识到它不需要改变) – 我猜你正在使用它作为一个代表性的例子。
在Fowler的重构书中 ,他讨论了用多态性替换条件。 这就是我认为替换if / switch语句(在适当的情况下)的好用途。
我不认为你在这里做一个公平的比较。
从它的外观来看,Anti-if活动只是为了实现更好的设计方法。
但是在您的情况下,您可以从上面的所有示例中看到,如果无法从曲面中移除并且始终存在于中心的某个位置。
那到底是为什么呢?
那么如果是生活的一般目的。 我并不是说如果每个地方都开始编码,但一般情况下如果没有区别,如果带来决定和目的,如果那不存在则那么世界上的每个对象都只会执行它的假设甚至不知道除此之外的其他任何东西。 很简单,你不会问这个问题。 🙂
我想你正在寻找工厂模式 。
你可以想象使用Method调用的地图代替上面的“策略”模式:
public class FooOrBar { private Map methodMap = new HashMap(); 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"); }
}
滥用三元运算符,至少在C#中:
Action result = s == "bar" ? (Action)(() => { Console.WriteLine("bar"); }): s == "foo" ? (Action)(() => { Console.WriteLine("foo"); }) : (Action)(() => { Console.WriteLine(); });
实际上,我接受了……从来没有这样做过。 使用开关。
我阅读http://www.antiifcampaign.com/articles/the-simplest-anti-if-code.html ,我认为这种药比疾病更糟糕。 更糟糕的是,更糟糕的是。 您需要在一些重型OO机器上预先投资,以解决可能的(不可能的?)未来问题。
派对有点晚了,但结合来自MRFerocius和cletus的C#词典答案给出了以下bmargulies答案的实现:
private Dictionary data = new Dictionary { {"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"](); }
- 如果字典中不存在该键,则在索引器中使用它将引发exception。
-
可以组成多个动作:
-
使用多行lambda语法可以对不同方法进行多次调用:
{"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}
-
这是我的。 使用LINQ和工厂模式:D
class FactoryString { static FactoryString() { private static Dictionary dictionary = new Dictionary { {"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")); }
我对这类问题的一般看法并不是说如果语句不好,那就是调试数据比调试代码更容易。
这是生产代码中的一个非平凡的例子。 乍一看,这看起来有点复杂,但其核心很简单:根据收费行上的处理代码,我们需要对某些相关句子行执行更新。 但是我们选择不同的句子行,并针对不同的处理代码对它们执行不同类型的更新。
这是一个相对简单的示例 – 只有五个处理代码,两个测试和两种类型的更新。 即便如此,这比它所取代的要简单得多。 此外,通过查看代码执行要求它应该执行的操作,更容易分辨,因为代码中的映射对应于需求文档中的表。 (在我编写这段代码之前,我不得不重写需求文档,以便这些东西都在一个表中定义。原始代码是一团糟,因为需求也很混乱。重写需求以使它们更清晰地暴露在要求也是。)
值得强调的是,编写覆盖100%此代码的unit testing非常容易。 值得强调的是,此代码的复杂性与它支持的处理代码,谓词和更新的数量呈线性关系; 如果使用case或if语句,它将按指数级扩展。
/// /// Update a sentence's status to Completed [401110] /// /// /// private static void CompleteSentence(DataRow senRow, DateTime eventDate) { senRow.SetField("SenStatus", "401110"); senRow.SetField("SenStatusDate", eventDate); } /// /// Update a sentence's status to Terminated [401120] /// /// /// private static void TerminateSentence(DataRow senRow, DateTime eventDate) { senRow.SetField("SenStatus", "401120"); senRow.SetField("SenStatusDate", eventDate); } /// /// Returns true if a sentence is a DEJ sentence. /// /// /// private static bool DEJSentence(DataRow senRow) { return Api.ParseCode(senRow.Field("SenType")) == "431320"; } /// /// Returns true if a sentence is a Diversion sentence. /// /// /// private static bool DiversionSentence(DataRow senRow) { return Api.ParseCode(senRow.Field ("SenType")).StartsWith("43"); } /// /// 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. /// private static readonly Dictionary> PDSentenceTests = new Dictionary> { {"411610", DiversionSentence}, // diversion successful {"413320", DEJSentence}, // DEJ successful {"442110", DiversionSentence}, // diversion unsuccessful {"442111", DiversionSentence}, // diversion unsuccessful {"442112", DiversionSentence}, // diversion unsuccessful {"442120", DEJSentence} // DEJ unsuccessful }; /// /// 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. /// private static readonly Dictionary> PDSentenceUpdates = new Dictionary> { {"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 ("PDDispositionCode")))) { string disp = chargeRow.Field ("PDDispositionCode"); foreach (DataRow s in CHGRows[chargeRow] .ChildRows("CAS-SUBCRM-CHG-SEN") .Where(x => PDSentenceTests[disp](x))) { PDSentenceUpdates[disp](s, EventDate); } } }