这个问题的动机是我最近开始看到的if..else if..else
结构。虽然它很简单并且有其用途,但它的一些东西不断告诉我它可以用更细粒度,更优雅的东西代替,通常更容易保持最新。
尽可能具体,这就是我的意思:
if (i == 1) {
doOne();
} else if (i == 2) {
doTwo();
} else if (i == 3) {
doThree();
} else {
doNone();
}
我可以想到两种简单的方法来重写它,或者通过三元(这只是编写相同结构的另一种方式):
(i == 1) ? doOne() :
(i == 2) ? doTwo() :
(i == 3) ? doThree() : doNone();
或使用Map(在Java中我认为也在C#中)或者字典或任何其他类似的K / V结构:
public interface IFunctor() {
void call();
}
public class OneFunctor implemets IFunctor() {
void call() {
ref.doOne();
}
}
/* etc. */
Map<Integer, IFunctor> methods = new HashMap<Integer, IFunctor>();
methods.put(1, new OneFunctor());
methods.put(2, new TwoFunctor());
methods.put(3, new ThreeFunctor());
/* .. */
(methods.get(i) != null) ? methods.get(i).call() : doNone();
事实上,上面的Map方法是我上次做的事情,但是现在我不能不再想到这个问题必须有更好的选择。
那么,替换if..else if..else的其他 - 并且最有可能更好的方法是在那里,哪一个是你最喜欢的?
好的,这是你的想法:
首先,最流行的答案是switch语句,如下所示:
switch (i) {
case 1: doOne(); break;
case 2: doTwo(); break;
case 3: doThree(); break;
default: doNone(); break;
}
这仅适用于可以在交换机中使用的值,至少在Java中这是一个非常有限的因素。但是,对于简单的案例,自然可以接受。
你似乎建议的另一种,也许有点更有趣的方法是使用多态。由CMS链接的Youtube讲座是一款出色的手表,请点击此处查看:"The Clean Code Talks -- Inheritance, Polymorphism, & Testing"据我所知,这将转化为类似的内容:
public interface Doer {
void do();
}
public class OneDoer implements Doer {
public void do() {
doOne();
}
}
/* etc. */
/* some method of dependency injection like Factory: */
public class DoerFactory() {
public static Doer getDoer(int i) {
switch (i) {
case 1: return new OneDoer();
case 2: return new TwoDoer();
case 3: return new ThreeDoer();
default: return new NoneDoer();
}
}
}
/* in actual code */
Doer operation = DoerFactory.getDoer(i);
operation.do();
Google演讲的两个有趣点:
此外,我认为值得一提的一个帖子是CDR,他提供了我不正常的习惯,虽然不建议使用,但看起来非常有趣。
谢谢大家的回答(到目前为止),我想我今天可能已经学会了一些东西!
答案 0 :(得分:21)
这些结构通常可以被多态性取代。这将为您提供更短,更简单的代码。
答案 1 :(得分:11)
开关声明:
switch(i)
{
case 1:
doOne();
break;
case 2:
doTwo();
break;
case 3:
doThree();
break;
default:
doNone();
break;
}
答案 2 :(得分:11)
在面向对象语言中,通常使用多态来替换if。
我喜欢这个涵盖主题的Google Clean Code Talk:
The Clean Code Talks -- Inheritance, Polymorphism, & Testing
摘要
您的代码是否包含if语句? 切换声明?你有吗? 各种各样的开关声明 地方呢?当你做出改变时,你做到了 发现自己做出了同样的改变 相同的if /切换几个 地方呢?你有没有忘记一个?
本演讲将讨论的方法 使用面向对象技术 删除许多这些条件。该 结果更清洁,更紧凑,更好 设计的代码更容易测试, 理解和维护。
答案 3 :(得分:5)
这个问题有两个部分。
如何根据值发送?使用switch语句。它最清楚地显示您的意图。
何时根据值调度?每个值只在一个位置:创建一个知道如何为值提供预期行为的多态对象。
答案 4 :(得分:5)
根据你所使用的东西的类型,考虑创建对象的层次结构并使用多态。像这样:
class iBase
{
virtual void Foo() = 0;
};
class SpecialCase1 : public iBase
{
void Foo () {do your magic here}
};
class SpecialCase2 : public iBase
{
void Foo () {do other magic here}
};
然后在你的代码中只需调用p-&gt; Foo(),就会发生正确的事情。
答案 5 :(得分:3)
switch声明当然比所有那些和其他人更漂亮。
答案 6 :(得分:3)
问题中给出的示例足以使用简单的开关。当if-elses嵌套越来越深时,就会出现问题。它们不再“清晰或易于阅读”(正如其他人所说)并且添加新代码或修复其中的错误变得越来越难以确定,因为如果逻辑逻辑可能不会达到预期的水平很复杂。
我已经看到这种情况发生了很多次(交换机嵌套了4层深度和数百行长 - 无法维护),特别是在工厂类中试图为太多不同的不相关类型做太多。< / p>
如果您所比较的值不是无意义的整数,而是某种唯一标识符(即使用枚举作为穷人的多态性),那么您希望使用类来解决问题。如果它们只是数值,那么我宁愿使用单独的函数来替换if和else块的内容,而不是设计某种人工类层次结构来表示它们。最终会导致代码比原始意大利面条更加混乱。
答案 7 :(得分:3)
我只是为了好玩而使用以下短手!如果代码清晰度比输入的字符数更多,请不要尝试其中任何一种。
对于doX()始终返回true的情况。
i==1 && doOne() || i==2 && doTwo() || i==3 && doThree()
当然,我试图确保大多数虚空函数返回1只是为了确保这些短手是可能的。
您还可以提供作业。
i==1 && (ret=1) || i==2 && (ret=2) || i==3 && (ret=3)
喜欢写作的
if(a==2 && b==3 && c==4){
doSomething();
else{
doOtherThings();
}
写
a==2 && b==3 && c==4 && doSomething() || doOtherThings();
如果不确定函数将返回什么,请添加|| 1: - )
a==2 && b==3 && c==4 && (doSomething()||1) || doOtherThings();
我仍然发现键入比使用所有if-else更快,它肯定会吓跑所有新的noobs。想象一下整整一页这样的声明,有5个缩进级别。
“if”在我的一些代码中很少见,我给它起了名字“if-less programming”: - )
答案 8 :(得分:3)
switch (i) {
case 1: doOne(); break;
case 2: doTwo(); break;
case 3: doThree(); break;
default: doNone(); break;
}
输入后,我必须说你的if语句没有那么多错。就像爱因斯坦所说的那样:“尽可能简单,但不要简单”。
答案 9 :(得分:3)
使用switch语句之外,可以更快,没有。如果Else清晰易读。不得不在地图中查看事物会混淆事物。为什么让代码更难阅读?
答案 10 :(得分:3)
在这种简单的情况下,您可以使用开关。
否则基于表格的方法看起来很好,只要条件足够常规以使其适用,这将是我的第二选择,特别是当案例数量很大时。
如果没有太多的情况,条件和行为是不规则的,那么多态性将是一种选择。答案 11 :(得分:3)
使用更清洁的开关/盒子:p
答案 12 :(得分:2)
switch
语句或具有虚函数的类作为花哨的解决方案。或者函数指针数组。这完全取决于条件的复杂程度,有时候如果不是这样的话。再次,创建一系列类以避免一个switch语句显然是错误的,代码应该尽可能简单(但不是更简单)
答案 13 :(得分:2)
我甚至会说没有程序应该使用其他程序。如果你这样做,你就是在寻找麻烦。你永远不应该假设它不是X它必须是Y.你的测试应该单独测试每个测试并且在测试之后失败。
答案 14 :(得分:1)
在OO范例中,你可以使用良好的旧多态来做到这一点。太大的if - else结构或switch结构有时被认为是代码中的气味。
答案 15 :(得分:1)
Map方法是最好的。它允许您封装语句并很好地分解。多态性可以补充它,但它的目标略有不同。它还引入了不必要的类树。
开关具有缺少break语句的缺点,并且真的鼓励不将问题分解成更小的部分。
话虽这么说:if..else的一棵小树很好(事实上,我赞成几天有3个if..elses而不是最近使用Map)。当你开始在其中加入更复杂的逻辑时,由于可维护性和可读性而成为一个问题。
答案 16 :(得分:1)
在python中,我会把你的代码写成:
actions = {
1: doOne,
2: doTwo,
3: doThree,
}
actions[i]()
答案 17 :(得分:1)
我将这些if-elseif -...构造视为“关键字噪音”。虽然可能很清楚它的作用,但缺乏简洁性;我认为简洁是可读性的重要部分。大多数语言都提供类似switch
语句的内容。构建一个地图是一种在没有这种语言的情况下得到类似东西的方法,但它确实感觉像是一种解决方法,并且有一些开销(一个switch语句转换为一些简单的比较操作和条件跳转,但是一个映射第一个是内置的,然后查询,然后才进行比较和跳转。
在Common Lisp中,内置了两个开关构造cond
和case
。 cond
允许任意条件,而case
仅测试相等性,但更简洁。
(cond ((= i 1) (do-one)) ((= i 2) (do-two)) ((= i 3) (do-three)) (t (do-none)))(case i (1 (do-one)) (2 (do-two)) (3 (do-three)) (otherwise (do-none)))
当然,您可以根据自己的需要制作自己的case
- 宏。
在Perl中,您可以使用for
语句,可选择使用任意标签(此处为:SWITCH
):
SWITCH: for ($i) {
/1/ && do { do_one; last SWITCH; };
/2/ && do { do_two; last SWITCH; };
/3/ && do { do_three; last SWITCH; };
do_none; };
答案 18 :(得分:1)
使用三元运算符!
三元运算符(53个字符):
i===1?doOne():i===2?doTwo():i===3?doThree():doNone();
如果(108Characters):
if (i === 1) {
doOne();
} else if (i === 2) {
doTwo();
} else if (i === 3) {
doThree();
} else {
doNone();
}
开关((甚至比以下更长!?!?)114字符):
switch (i) {
case 1: doOne(); break;
case 2: doTwo(); break;
case 3: doThree(); break;
default: doNone(); break;
}
这就是你所需要的!它只有一条线,它非常整洁,比开关短,如果!
答案 19 :(得分:0)
当然,这个问题依赖于语言,但在许多情况下,switch语句可能是更好的选择。一个好的C或C ++编译器将能够生成jump table,这对于大型案例集来说会明显更快。
答案 20 :(得分:0)
如果你真的必须有一堆if测试,并且想要在测试为真时做不同的事情,我会推荐一个while循环,只有ifs-别的。每个if如果测试调用方法然后突破循环。没有别的东西比一堆堆叠的if / else / if / else等更糟糕。