反竞选活动

时间:2009-07-22 19:23:09

标签: design-patterns

我最近遇到了一个非常有趣的网站,表达了一个非常有趣的想法 - 反竞选活动。你可以在www.antiifcampaign.com看到这个。我必须同意复杂的嵌套IF语句是后方的绝对痛苦。我目前正在进行一个项目,直到最近才有一些疯狂的嵌套IF向右滚动相当多的方式。我们以两种方式解决了问题 - 我们使用Windows Workflow Foundation来解决路由(或工作流程)问题。我们正在利用ILOG Rules for .NET(最近由IBM购买!!)实施所有业务规则。这在很大程度上已经治愈了我们嵌套的IF痛苦...但我发现自己想知道有多少人以AntiIfCampaign的优秀人士建议(see an example here)的方式通过创建大量的抽象类来治愈他们的痛苦表示最初由嵌套IF覆盖的给定方案。我想知道解决这种复杂性的另一种方法是否也可能是使用诸如StructureMap之类的IoC容器来移入和移出不同的功能。无论哪种方式......

问题:鉴于我有一个嵌套的复杂IF或SWITCH语句,用于评估给定类型的事物(比如评估枚举),以确定我想如何处理枚举类型的那种东西 - 在不使用IF或SWITCH分层结构的情况下,采用相同形式的处理有哪些方法?

public enum WidgetTypes
{
    Type1,
    Type2,
    Type3,
    Type4
}

...

WidgetTypes _myType = WidgetTypes.Type1;

...

switch(_myType)
{
    case WidgetTypes.Type1:
        //do something
        break;

    case WidgetTypes.Type2:
        //do something
        break;

    //etc...
}

16 个答案:

答案 0 :(得分:59)

问题不在于'if'语句,而是编写错误代码的程序员。

编辑:另外,正如其他人所指出的那样,在使用if语句来检查对象的类型时,应该使用多态(如果可用),但是如果语句本身和是非常有用和基本的结构。

答案 1 :(得分:36)

在Java中,很容易将枚举用作多态反if代理。

public class AntiIf {
  public enum WidgetTypes {
    Type1 {
      public void doSomething() {
        //...
      }},
    Type2 {
      public void doSomething() {
        //...
      }},
    Type3 {
      public void doSomething() {
        //...
      }},
    Type4 {
      public void doSomething() {
        //...
      }};

    public abstract void doSomething();
  }

  WidgetTypes _myType; // set by someone to one of the types.

  public void someFunction() {
    //...
    _myType.doSomething();
    //...
  }
}

答案 2 :(得分:21)

反对如果是愚蠢的。

有时候,通过多态替换条件是正确的,但在这些情况下, if 语句不是真正的问题。真正的问题是以非抽象的方式处理抽象类型,即未能在基类的抽象层次上进行思考。

其他时候可以替换条件via多态,但这样做是个坏主意。导致您将一种类型与另一种类型区别对待的逻辑可能属于算法本身,而不属于单个类。将该逻辑移到类中可能会导致类的代码过度地意识到它们的使用环境。

但大多数情况下, if 语句与多态无关。摆脱 if 语句是错误的目标。

反对如果是愚蠢的。

答案 3 :(得分:16)

答案 4 :(得分:14)

切换语句通常可以用多态替换:

abstract class WidgetBase {
  public abstract void DoSomething();
}

class Widget1 : WidgetBase {
  public override void DoSomething() {
    // Do something ...
  }
}

class Widget2 : WidgetBase {
  public override void DoSomething() {
    // Do something ...
  }
}

...

Widget widget = new Widget1(); // Use a factory pattern instead.
widget.DoSomething();

答案 5 :(得分:12)

您可以使用Command Pattern根据某些值或输入执行命令。

在回答您的问题时,您可以使用简单的查找表或多态来实现此目的,尽管它将逻辑放在程序的不同部分,这可能确实是正确的事情。以下是C#中的几个示例:

var commands = new Dictionary<WidgetTypes, Action>()
    {
        { WidgetTypes.Type1, () => Console.WriteLine("Type 1") },
        { WidgetTypes.Type2, () => Console.WriteLine("Type 2") },
        { WidgetTypes.Type3, () => Console.WriteLine("Type 3") },
        { WidgetTypes.Type4, () => Console.WriteLine("Type 4") }
    };

commands[WidgetTypes.Type1]();

public interface ICommandHandler
{
    void HandleCommand();
}

public class Command1Handler : ICommandHandler
{
    public void HandleCommand()
    {
        Console.WriteLine("Type 1");
    }
}

// ...

var commands = new Dictionary<WidgetTypes, ICommandHandler>()
    {
        { WidgetTypes.Type1, new Command1Handler() },
        { WidgetTypes.Type2, new Command2Handler() },
        { WidgetTypes.Type3, new Command3Handler() },
        { WidgetTypes.Type4, new Command4Handler() }
    };

commands[WidgetTypes.Type1].HandleCommand();

一个奖励反射方法,用于调用当前对象上具有相同名称的方法:

public void Type1()
{
    Console.WriteLine("Type 1");
}

//...

var type = WidgetTypes.Type2;
typeof(MyClass).GetMethod(type.ToString()).Invoke(this, new object[] { });

注意:实际上并没有这样做

答案 6 :(得分:6)

...什么?

我无法想象如何在没有某种比较的情况下做这样的事情。完全没有。

我可以想象一个反goto运动,但如果对我来说似乎是一个非常需要的控制流程结构。

答案 7 :(得分:6)

不要担心简单的if语句或直接的switch语句。

担心这个:

  1. 可怕地嵌套if语句(箭头反模式)。这些东西无法读取和调试。
  2. if(或case)中带有bagilion代码行。 “好的,我们这样做了......在这种情况下,那个......那个......在这种情况下。也很难读。几乎不可能测试或调试。
  3. 超级复杂的条件:如果(碗和KitchenSink或浴室配件和......)。你还在测试什么?在方法中包装 - 在这种情况下:CanHoldWater()。

答案 8 :(得分:3)

这取决于你如何处理这种情况。如果使用相同的签名调用不同的函数,则可以使用字典,其中键是类型,而值是指向真实处理程序的委托。因此,你可以摆脱开关,只是做

_myDictionary[ _myType ]( param1, ... );

答案 9 :(得分:2)

用类似C语言实现这个想法是非常蹩脚的......但是在一个适当的功能语言中,保护模式匹配更加惯用,表达条件的方式更加丰富,我敢补充。

答案 10 :(得分:2)

当逻辑必须被烘焙到类中时,多态性是正确的方法。

但是,由于目前许多应用程序已经与自己的数据库进行交互,因此另一种解决方案是使用基于表的逻辑。使用表的优点:

  1. 调整业务逻辑,无需重新编译。
  2. 数据库实例可以共享相同的代码但逻辑不同。
  3. 谈论直接在数据库varchar()字段中嵌入源代码并动态编译它。

    但是假设您有一个输入字段列表,并且对于每个输入字段,您需要执行(a)一些RegEx来清除值,以及(b)一个或多个测试来验证结果。

    在纯代码世界中,每种类型的字段都有一个子类,每个字段都实现了一个名为ScrubValue()和Validate()的方法。

    在基于表格的模式中,单个Field类将基于FieldType属性进入数据库(当然是缓存)并检索一组RegEx搜索/替换字符串,以及验证测试参数列表(最小和最大长度,匹配的正则表达式等等。)。

    这适用于子类型数量随时间增加的任何情况,逻辑可以表示为一组可配置的字符串,数字等,而不必直接实现为代码。

答案 11 :(得分:1)

哇......这是真的吗?

当你走错了执行路径时,你可以尝试绝对捕获每一个可能的异常,直到你找到一条不会破坏的执行路径,但那比FAR更差。

当面向对象和多态是自然的时,您可以使用命令模式或映射,但是如果您处理的是来自用户输入的结果,这些结果是复杂的而不是自身的对象呢?

答案 12 :(得分:1)

一旦你的if语句开始变得庞大和嵌套,可能的一个可能性就是你必须测试的条件数量增加了。在这种情况下,当您为情况添加更多条件时,它开始变得指数级地更容易让某些条件组合陷入困境,然后是bam,您自己就是一个错误。

我一直认为在这样的情况下,某种真值表会很好。关于我可以提出的最接近的近似值,但最终会让普通程序员感到困惑。它涉及将所有条件组合成一个整数,然后尝试覆盖使用switch语句或查找表创建的范围中的每个数字。

我不确定多态是这个问题的正确答案。任何人都有更好的想法吗?

答案 13 :(得分:0)

案例陈述非常适合组织嵌套ifs。

答案 14 :(得分:0)

我想我理解他们的意思。如果没有if和switch,我们可能无法生存,但 为真,深度嵌套的条件块很难阅读。

我自己的工作中的一个例子:计算分流惠斯通电桥模拟应变的函数。这个功能需要5个配置参数(分流分支,有任何分流检测线,电源检测线......),我不太期待编写调度程序。

我提出了一个模板化的代码结构,允许我“声明”正确的函数(大约30个左右)而不必将它们压缩成一个调度例程。

这使我们能够非常快速添加新的配置参数案例,并从数学概述中“复制”方程式。真是一场胜利。

答案 15 :(得分:0)

Strongly Typed Enum pattern有助于摆脱对枚举值的switch语句。只需将方法添加到强类型的枚举类中即可。

WidgetType.DoSomething();