处理嵌套的if-then-else/嵌套的switch语句

11

有没有设计模式/方法/方式可以消除嵌套的if-then-else条件/switch语句?

我记得在Google的一篇博客文章中看到过一些方法,但现在好像找不到了。


你能举个例子吗? - Apocalisp
7个回答

8

你读过Coding Horror上关于简化箭头代码的this吗?

如果你使用一种没有异常处理机制的编程语言,可以将throw替换为return或goto。


1
+1守卫子句是减少嵌套if语句并尽早退出函数的好方法。在我看来,这样更加清晰易读。 - JoshBerke

6

您希望使用重构来替换使用多态类的条件语句,例如example

或者这里有另一个example

基本上,理想情况下非常简单,您创建一个对象层次结构,并将各种行为移动到重写方法中。您仍需要一个方法来创建正确的类,但可以使用工厂模式来完成此操作。

编辑

让我补充一下,这并不是每种情况下都是完美的解决方案。正如(抱歉,我忘记您的名字了)在我的评论中指出的那样,有时这可能很麻烦,特别是如果您必须创建一个对象模型才能做到这一点。如果您拥有以下内容,则此重构将非常出色:

function doWork(object x)
{

   if (x is a type of Apple)
   {
      x.Eat();

   } else if (x is a type of Orange)
   {
      x.Peel();
      x.Eat();
   }

}

在这里,您可以将开关重构为每种水果都会处理的新方法。

编辑

正如有人指出如何创建正确的类型以进入doWork,有更多解决此问题的方法,我可能无法列出一些基本方法。第一个和最直接的(是的,违反了这个问题的规则)是一个开关:

class FruitFactory
{
   Fruit GetMeMoreFruit(typeOfFruit)
   {
         switch (typeOfFruit)
         ...
         ...
   }
}

这种方法的好处是易于编写,通常是我使用的第一种方法。虽然你仍然需要一个switch语句,但它只在代码的一个区域中被隔离出来,并且非常基本,它只返回一个对象。如果你只有几个对象而且它们不会改变,那么这种方法非常有效。
其他更复杂的模式可以研究抽象工厂。如果你的平台支持,你也可以动态创建水果。你还可以使用类似提供程序模式的东西。对我来说,这意味着你配置了你的对象,然后你有一个工厂,根据配置和你给工厂的关键字动态地创建正确的类。

如果你手头有对象和多态,这个工作就很容易。如果没有,那就会很痛苦。 - Adam Hawes
是的,这很正确。如果逻辑相当简单,那么可能不值得费力去拆分它...但是,如果每个条件体内有很多操作,那么将其拆分出来可以减少您需要关注的复杂性。 - JoshBerke

3
我在2008年4月份的博客中已经写过如何解决这些问题了。请看这里,并告诉我你的想法。
我建议您:
  1. 使用多态性来获得所需的正确运行时行为,而无需条件语句。

  2. 将所有条件语句移到某种“工厂”中,在运行时将适当的类型交给您。

  3. 完成了。是不是很容易?:)

如果您想查看一些实际的代码示例,以了解如何转换您的代码,请访问我的博客。
P.S. 这不是自我推销的廉价尝试;我已经是SO用户很长时间了,这是我第一次链接到我的博客 - 我之所以这样做,是因为我认为这是相关的。

1
如果你摘录文章,或者至少提及本篇文章的主要观点(而不是强迫用户点击链接才能了解内容),那么你可能会获得更多赞同。 - Adam Davis
@Adam:是的,我本来想在第一次编辑答案时就这么做的,但现在已经很晚了,我也很累了。不过,我还是按照你的建议去做了。谢谢! - Esteban Araya
有用的;然而,它只涵盖了一种if-else箭头的类型。 - Adrian

2

1

你可能想要看一下策略模式,它可以避免使用长串的if语句和链接条件。相反,你可以将每个条件抽象成一个不同的对象,每个对象定义其特定的行为。

定义这些对象的类将实现一个接口,该接口将由父对象调用。


1

您没有说明使用的是哪种编程语言,但如果您正在使用诸如C++、C#或Java之类的面向对象编程语言,通常可以使用虚函数来解决与switch语句相同的问题,并且以更具扩展性的方式实现。以C++为例:

class X {
public:
    int get_type();     /* Or an enum return type or similar */
    ...
};

void eat(X& x) {
    switch (x.get_type()) {
    TYPE_A: eat_A(x); break;
    TYPE_B: eat_B(x); break;
    TYPE_C: eat_C(x); break;
    }
}

void drink(X& x) {
    switch (x.get_type()) {
    TYPE_A: drink_A(x); break;
    TYPE_B: drink_B(x); break;
    TYPE_C: drink_C(x); break;
    }
}

void be_merry(X& x) {
    switch (x.get_type()) {
    TYPE_A: be_merry_A(x); break;
    TYPE_B: be_merry_B(x); break;
    TYPE_C: be_merry_C(x); break;
    }
}

使用

class Base {
    virtual void eat() = 0;
    virtual void drink() = 0;
    virtual void be_merry() = 0;
    ...
};

class A : public Base {
public:
    virtual void eat() { /* Eat A-specific stuff */ }
    virtual void drink() { /* Drink A-specific stuff */ }
    virtual void be_merry() { /* Be merry in an A-specific way */ }
};

class B : public Base {
public:
    virtual void eat() { /* Eat B-specific stuff */ }
    virtual void drink() { /* Drink B-specific stuff */ }
    virtual void be_merry() { /* Be merry in an B-specific way */ }
};

class C : public Base {
public:
    virtual void eat() { /* Eat C-specific stuff */ }
    virtual void drink() { /* Drink C-specific stuff */ }
    virtual void be_merry() { /* Be merry in a C-specific way */ }
};

优点是您可以添加新的Base派生类DEF等,而无需触及任何仅涉及指向或引用Base的代码,因此没有任何东西会过时,就像在原始解决方案中switch语句一样。 (在Java中,转换看起来非常相似,其中方法默认为虚拟方法,我确信在C#中也是如此。)在大型项目中,这是一个巨大的可维护性优势。


可能是因为虚函数是C++实现多态性的方式;+1计数反对者的懦弱行为。 - Steven A. Lowe
@Steven: 我猜...我希望这显而易见适用于任何面向对象的语言(我知道Java版本几乎相同,但默认情况下方法是“virtual”的,C#可能也是一样),但无论如何我已经编辑以使其更加明确。 - j_random_hacker

0
怎么样:
/* Code Block 1... */

if(/* result of some condition or function call */)
{
   /* Code Block 2... */

   if(/* result of some condition or function call */)
   {
      /* Code Block 3... */

      if(/* result of some condition or function call */)
      {
         /* Code Block 4... */
      }
   }
}

变成这样:

/* Code Block 1... */
IsOk = /* result of some condition or function call */

if(IsOK)
{
   /* Code Block 2... */
   IsOk = /* result of some condition or function call */
}

if(IsOK)
{
   /* Code Block 3...*/
   IsOk = /* result of some condition or function call */
}

if(IsOK)
{
   /* Code Block 4...*/
   IsOk = /* result of some condition or function call */
}

/* And so on... */

如果合适的话,当IsOk变为false时,您当然可以返回。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接