避免if语句

时间:2009-08-26 21:21:30

标签: oop if-statement

我今天正在考虑面向对象的设计,我想知道你是否应该避免if语句。我的想法是,在任何需要if语句的情况下,您只需创建两个实现相同方法的对象。这两个方法实现只是原始if语句的两个可能分支。

我意识到这似乎是极端的,但似乎你可以尝试在某种程度上争论它。有什么想法吗?

修改

哇,没多久。我想这太极端了。是否有可能说,在OOP下你应该期望更少的陈述?

第二次编辑

这是什么:一个基于其属性确定其方法实现的对象。也就是说,您可以通过两种方式实现someMethod()并指定一些限制。在任何时候,对象都将根据其属性路由到正确的方法实现。因此,在if(x > 5)的情况下,只有两种方法依赖于x属性

24 个答案:

答案 0 :(得分:36)

我可以告诉你一件事。无论人们怎么说,考虑简化和消除不必要的分支都是您成为软件开发人员的标志。分支错误,测试,维护,错误率较高等原因有很多。这是我在面试人员时所寻求的事情之一,也是他们作为开发人员成熟程度的绝佳指标。我鼓励您继续尝试,通过使用更少的条件来简化代码和设计。当我执行此切换时,我发现调试代码的时间要少得多,它只是起作用,然后当我不得不改变某些内容时,由于大多数代码都是顺序的,因此更改非常容易。无论别人怎么说,我都鼓励你100%继续做你正在做的事情。请记住,大多数开发人员的工作和思考程度要低得多,并且只遵循规则。这样做很好。

答案 1 :(得分:12)

解释如何在没有if语句或三元逻辑的情况下实现以下内容:

if ( x < 5 ) {
   x = 0
} else {
   print x;
}

答案 2 :(得分:9)

是的,通过复杂的条件可以通过多态性简化。但它一直没用。去阅读福勒的重构书,了解何时。

http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html

答案 3 :(得分:8)

完全消除if语句是否不现实,我认为这不是Ori所暗示的。但它们通常可以使用多态来替换。 (许多转换语句也是如此)。

Francesco Cirillo启动Anti-If Campaign以提高对此问题的认识。他说:

  

了解如何使用对象可以让开发人员根据类型消除IF,这些IF通常会损害软件的灵活性和发展能力。

您或您的团队也可以join the campaign

答案 4 :(得分:6)

我的一位老师曾经这么说过。我倾向于认为那些对这种事情如此教条的人通常不会以谋生为目的。

答案 5 :(得分:5)

看一下the Anti-If Campaign我们的想法是不要在你的应用程序中用策略或状态模式替换每一个。这个想法是,当你有一个复杂的分支逻辑时,特别是基于枚举的东西,你应该考虑重构战略模式。

在这种情况下,您可以使用Factory删除if all。这是一个相对简单的例子。当然,正如我在一个实际案例中所说的那样,你的策略中的逻辑将比打印出“我是主动的”更复杂。

public enum WorkflowState
{
  Ready,
  Active,
  Complete
}

public interface IWorkflowStrategy
{
  void Execute();
}

public class ActiveWorkflowStrategy:IWorkflowStrategy
{
  public void Execute()
  {
    Console.WriteLine("The Workflow is Active");
  }
}

public class ReadyWorkflowStrategy:IWorkflowStrategy
{
  public void Execute()
  {
    Console.WriteLine("The Workflow is Ready");
  }
}

public class CompleteWorkflowStrategy:IWorkflowStrategy
{
  public void Execute()
  {
    Console.WriteLine("The Workflow is Complete");
  }
}

public class WorkflowStrategyFactory
{
  private static Dictionary<WorkflowState, IWorkflowStrategy> _Strategies= 
    new Dictionary<WorkflowState, IWorkflowStrategy>();
  public WorkflowStrategyFactory()
  {
    _Strategies[WorkflowState.Ready]=new ReadyWorkflowStrategy();
    _Strategies[WorkflowState.Active]= new ActiveWorkflowStrategy();
    _Strategies[WorkflowState.Complete = new CompleteWorkflowStrategy();
  }
  public IWorkflowStrategy GetStrategy(WorkflowState state)
  {
    return _Strategies[state];
  }
}

public class Workflow
{
    public Workflow(WorkflowState state)
    {
        CurrentState = state;
    }
    public WorkflowState CurrentState { get; set; }
}

public class WorkflowEngine
{
    static void Main(string[] args)
    {
        var factory = new WorkflowStrategyFactory();
        var workflows =
            new List<Workflow>
                {
                    new Workflow(WorkflowState.Active),
                    new Workflow(WorkflowState.Complete),
                    new Workflow(WorkflowState.Ready)
                };
        foreach (var workflow in workflows)
        {
            factory.GetStrategy(workflow.CurrentState).
                Execute();
        }
    }
}   

答案 6 :(得分:5)

避免陈述:有很多方法可以做,其中一种方法如下:

int i=0;
if(i==1)
{
//Statement1
}

if(i==2)
{
//Statement2
}

if(i==3)
{
//Statement3
}

if(i==4)
{
//Statement4
}

使用Dictionary和delegate:

delegate void GetStatement ();

Dictionary<int,GetStatement > valuesDic=new Dictionary<int,GetStatement >();

void GetStatement1()
{
//Statement1
}
void GetStatement2()
{
//Statement2
}
void GetStatement3()
{
//Statement3
}


void GetStatement4()
{
//Statement4
}

void LoadValues()
{
valuesDic.Add(1,GetStatement1);
valuesDic.Add(2,GetStatement2);
valuesDic.Add(3,GetStatement3);
valuesDic.Add(4,GetStatement4);

}

更换If Statement:

int i=0;
valuesDic[i].Invoke();

答案 7 :(得分:4)

在某些方面,这可能是一个好主意。当您可以使用虚拟功能时,在对象内的类型字段上切换通常是个坏主意。但是虚函数机制绝不是为了取代if()测试。

答案 8 :(得分:3)

这取决于原始陈述的比较。我的经验法则是,如果它是针对枚举的switchif测试相等,那么这是一个单独方法的良好候选者。但是,switchif语句用于许多其他类型的测试 - 没有好方法可以替换关系运算符(<>,{{ 1}},<=)使用专门的方法,并且某些类型的枚举测试在标准语句中效果更好。

因此,如果它们看起来像这样,您应该只替换>=

if

答案 9 :(得分:3)

回答ifTrue的问题:

好吧,如果你有开放的类和足够强大的依赖类型系统,那很容易,如果有点傻。非正式而且没有特定的语言:

class Nat {
    def cond = {
        print this;
        return this;
    }
}

class NatLessThan<5:Nat> { // subclass of Nat
    override cond = {
        return 0;
    }
}

x = x.cond();

(续)

或者,没有开放课程,但假设有多个派遣和匿名课程:

class MyCondFunctor {
    function branch(Nat n) {
        print n;
        return n;
    }

    function branch(n:NatLessThan<5:Nat>) {
        return 0;
    }
}

x = new MyCondFunctor.branch(x);

或者,和以前一样但是使用匿名类:

x = new {
    function branch(Nat n) {
        print n;
        return n;
    }

    function branch(n:NatLessThan<5:Nat>) {
        return 0;
    }
}.branch(x);

当然,如果你重构那个逻辑,你会有更轻松的时间。请记住,存在完全图灵完备型系统。

答案 10 :(得分:3)

如果没有if语句,如何决定使用哪个对象的方法?

答案 11 :(得分:2)

Assume we have conditional values.

public void testMe(int i){

if(i=1){
somevalue=value1;
}


if(i=2){
 somevalue=value2;
}

if(i=3){
somevalue=value3;
}

}

//**$$$$$you can replace the boring IF blocks with Map.$$$$$**

// ============================================= ===============

Same method would look like this:
--------------------------------
public void testMe(int i){

Map<Integer,String> map = new HashMap<Integer,String>();

map.put(1,value1);
map.put(2,value2);
map.put(3,value3);

}
This will avoid the complicated if conditions.

使用工厂模式加载类时,可以使用simliar解决方案。

public void loadAnimalsKingdom(String animalKingdomType)
    if(animalKingdomType="bird"){
    Bird b = new Bird();
    }
    if(animalKingdomType="animal"){
    Animal a= new Animal();
    }
    if(animalKingdomType="reptile"){
    Reptile r= new Reptile();
    }
  }

现在使用map:

   public void loadAnimalsKingdom(String animalKingdomType)
    {
       Map <String,String> map = new HashMap<String,String>();

       map.put("bird","com.animalworld.Bird.Class");
       map.put("animal","com.animalworld.Animal.Class");
       map.put("reptile","com.animalworld.Reptile.Class");

       map.get(animalKingdomType);

***Use class loader to load the classes on demand once you extract the required class from the map.***
}

喜欢解决方案吗?竖起大拇指。 - Vv

答案 12 :(得分:2)

else创建一个全新的类,虽然技术上可行,但可能会导致代码难以阅读,维护甚至证明是正确的。

答案 13 :(得分:1)

我认为他所说的或者他的意思是他认为最好避免过度滥用“标记”并通过几个if语句向类添加自定义功能,这对于子类或重新考虑对象层次结构。

答案 14 :(得分:1)

这是一个有趣的想法。我认为你可能理论上会这样做,但对于一种不是专门用来支持它的语言来说,这将是一个巨大的痛苦。我当然看不出任何理由。

答案 15 :(得分:0)

你必须明白(x > 5)的真正含义。假设x代表一个数字,那么它基本上是&#34;分类&#34;所有数字大于五。因此代码在使用python语法的语言中看起来像这样:

class Number(Object):

    # ... Number implementation code ... #

    def doSomething():
        self = 0
        return self

    def doSomethingElse():
        pass

class GreaterThan5(Number):
    def doSomething():
        print "I am " + self

    def doSomethingElse():
        print "I like turtles!"

然后我们可以运行如下代码:

>>> type(3)
<class Number>
>>> type(3+3)
<class GreaterThan5>
>>> 3.doSomething()
0
>>> (3 + 3).doSomething()
I am 6
>>> (7 - 3).doSomethingElse()
>>>

此处的自动类型转换非常重要。据我所知,今天没有一种语言允许你这么乱用整数。

最后,您可以在代码中执行任何操作。只要阅读它的人能够立即理解。所以对整数或任何非常规的多态调度必须有很好的推理背后。

答案 16 :(得分:0)

我的两点是我对面向对象方法的理解 -

首先,程序中的哪些对象应该是直观的。也就是说,我不应该尝试创建一个'Arithmatic'类来提供数学函数。这是滥用OOD。

第二,这是我的一个非常强烈的意见。它不应该被称为面向对象设计,而是对象面向方法的设计!如果对象的方法名称本身不直观,那么继承的对象可能最终会重新实现已有的方法。

据我所知,面向对象的方法并不是程序方法的替代品。相反,它主要是出于语言创造者的两个主要原因 -

  1. 更好地确定变量的范围。

  2. 更好的垃圾收集功能,而不是拥有太多的全局变量。

答案 17 :(得分:0)

我同意Vance认为IF不好,因为它会增加条件复杂性,应该尽可能避免。

多态性是一种完全可行的解决方案,条件是用它来理解而不是&#34;避免如果&#34;。

附注不符合您的OOP要求,但面向数据的方法也倾向于avoid the branching

答案 18 :(得分:0)

我最近一直在关注反对谈话,这对我来说听起来像极端/夸张的言论。但是我认为这句话中有道理:通常if语句的逻辑可以通过多态实现更合适。我认为每次修改if语句时都要记住这一点。话虽如此,我认为if语句仍然是一个核心逻辑结构,不应该担心或避免作为一个原则。

答案 19 :(得分:0)

if语句是编程的核心,简而言之,你无法明智地避免它们。

然而,OOP的一个关键目标 - 事实上,其中一个“支柱” - 是封装。旧的“封装变化的”规则可以帮助您删除那些麻烦的if和case语句,其中您试图考虑对象中的每个选项。处理分支,特殊情况等的更好的解决方案是使用类似“工厂”设计模式(抽象工厂或工厂方法 - 当然根据需要)。

例如,不是让你的主代码循环检查你使用if语句的OS然后分支创建每个操作系统具有不同选项的GUI窗口,你的主代码将从工厂创建一个对象,它使用操作系统来确定要制作的特定于OS的具体对象。在执行此操作时,您将从主代码循环中取出变量(以及长if-then-else子句)并让子对象处理它 - 因此下次需要进行更改(例如支持新操作系统)时,您只需从工厂界面添加一个新类。

答案 20 :(得分:0)

我会说答案很模糊,是的。特别是当语言允许一些重型函数式编程时(即C#,F#,OCaml)。

包含2个if语句的组件强烈耦合两个业务规则,因此请将其分解。

以此作为一般的经验法则,但我同意。如果你有一堆if语句,也许你应该考虑另一种方法。

答案 21 :(得分:0)

我认为将这个论点应用于每个if语句的概念是非常极端的,但是某些语言使你能够在某些场景中应用这个想法。

这是我过去为固定大小的双端队列(双端队列)编写的Python实现示例。除了创建“删除”方法并在其中包含if语句以查看列表是否已满之外,您只需创建两个方法并根据需要将它们重新分配给“删除”功能。

以下示例仅列出“删除”方法,但显然还有“追加”方法等。

class StaticDeque(collections.deque):

    def __init__(self, maxSize):

        collections.deque.__init__(self)
        self._maxSize = int(maxSize)
        self._setNotFull()

    def _setFull(self):

        self._full = True
        self.remove = self._full_remove

    def _setNotFull(self):

        self._full = False
        self.remove = self._not_full_remove

    def _not_full_remove(self,value):

        collections.deque.remove(self,value)

    def _full_remove(self,value):

        collections.deque.remove(self,value)
        if len(self) != self._maxSize and self._full:
            self._setNotFull()

在大多数情况下,这不是一个有用的想法,但有时它可能会有所帮助。

答案 22 :(得分:0)

当然,无论你做什么,都需要进行某种形式的比较?最后......确保你可以避免使用if语句,但是你要使用if语句为代码生成IDENTICAL代码。

有人纠正我,如果我错了,但我想不出你可以获得任何胜利形式的时间。

答案 23 :(得分:0)

相当极端。做你想要的东西会导致很多不必要的代码重复,除非整个功能完全不同,基于单个环境if;如果是这样的话,那应该是在方法调用的另一端。

if语句肯定在面向对象设计中占有一席之地。