11

是否有任何设计模式/方法/方法来删除嵌套的 if then else 条件/switch 语句?

我记得在一篇谷歌代码博客文章中遇到过谷歌人员使用的一些方法。不过现在好像找不到了

4

7 回答 7

8

你读过Coding Horror 的扁平化箭头代码吗

如果您使用的语言没有例外,则可以将 throw 替换为 return 或 goto。

于 2009-01-30T04:57:29.793 回答
6

您想使用重构来替换使用多态类的条件。例如. _

或者这是另一个例子

本质上,理想非常简单,您创建一个对象层次结构,并将各种行为移动到一个覆盖的方法中。您仍然需要一种方法来创建正确的类,但这可以使用工厂模式来完成。

编辑

让我补充一点,这并不是在每种情况下都是完美的解决方案。正如(对不起,我忘记了你的名字)在我的评论中指出的那样,有时这可能会很痛苦,特别是如果你必须创建一个对象模型来做到这一点。如果你有这个,这个重构会很出色:

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 语句,但它与一个代码区域隔离,并且非常基本,它返回的只是一个对象。如果您只有几个对象并且它们不更改,则效果很好。

您可以研究的其他更复杂的模式是Abstract Factory。如果您的平台支持,您也可以动态创建 Fruit。您也可以使用Provider Pattern之类的东西。这对我来说本质上意味着你配置你的对象,然后你有一个基于配置的工厂和一个你给工厂动态创建正确类的键。

于 2009-01-30T04:49:46.033 回答
3

实际上,我在 2008 年 4 月的博客中写过如何解决这个问题。看看这里,让我知道你的想法。

我推荐你:

  1. 使用多态性在没有条件语句的情况下获得所需的正确运行时行为。

  2. 获取所有条件语句,并将它们移动到某种“工厂”中,它将在运行时为您提供适当的类型。

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

如果您想查看一些有关如何转换代码的实际代码示例,请访问我的博客。

PS这不是一次廉价的自我推销尝试;很长时间以来,我一直是 SO 用户,这是我第一次链接到我的博客 - 我这样做只是因为我认为它是相关的。

于 2009-01-30T04:58:15.347 回答
2

你在想 Google 的“ The Clean Code Talks -- Inheritance, Polymorphism, & Testing ”视频吗?它讨论了使用面向对象技术来删除 if/switch 条件的方法。

于 2009-01-30T05:00:08.870 回答
1

你没有说你使用的是什么语言,但如果你使用的是 OO 语言,例如 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派生类D、等等,而不必触及任何只处理指针或引用的代码E,因此不会像原始语句中的语句那样过时。解决方案。(这种转换在 Java 中看起来非常相似,默认情况下方法是虚拟的,我相信在 C# 中它看起来也很相似。)在一个大型项目中,这是一个巨大的可维护性胜利。FBaseswitch

于 2009-01-30T04:56:00.280 回答
1

您可能想查看Strategy Pattern,其中不是放置带有链接条件的长链 ifs,而是将每个条件抽象为不同的对象,每个条件定义其特定行为。

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

于 2009-01-30T05:19:36.647 回答
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,您当然可以返回。

于 2009-01-30T19:44:01.720 回答