如果更少的编程(基本上没有条件)


67

我有一个同事告诉我,他曾经在一家公司工作过,该公司的政策是永远在代码中没有条件(“ if”和“ switch”语句),并且他们让代码中的所有决定都使用多态性和(我猜)其他一些面向对象原则。

有点理解这背后的原因,即拥有更干且更易于更新的代码,但我正在寻找对此概念的更深入的说明。也许这是更通用的设计方法的一部分。

如果有人对此有任何资源,或者愿意解释甚至有更多与此相关的术语,我可以使用它来寻找更多的答案,我将不得不这样做。

在SO上发现了一个与之相关的问题,但是我不熟悉C ++,所以我对那里的答案不太了解。

(我不是OO大师,但我可以管理)

我最精通PHP,在Python之后,我更喜欢使用这些语言的信息。

更新:我将向我的同事询问有关他确切含义的更多信息。

2015年更新:经过多年的编程经验,我现在看到该策略的目的可能是防止程序员通过在某些地方仅添加条件(如果语句)来随意添加功能。扩展软件的更好方法是使用“打开/关闭原则”,其中通过使用继承和多态性来扩展软件。我强烈怀疑该政策是否在所有条件上都过于严格,因为如果没有这些条件,很难完全执行。


16
您的同事说的这家公司是什么?我问,所以我可以避免他们。流控制语句是编程的基础。如果我什至不能在最适合完成工作的时候使用最基本的工具,那么我还能使用什么呢?是的,您可以滥用if语句是对的,但是其他所有情况都是如此。
in silico 2011年

15
@In silico-流控制语句是过程编程的基础。多态性是面向对象编程的基础。也就是说,我们大多数人都使用这两种范式。
TrueWill 2011年

6
@TrueWill:流程控制是命令式编程的基础,并且OOP与命令式编程一样,也是基于命令式编程的。即使当您进行“纯粹的” OOP操作时,您仍然可以用稍微多一点的OOP-y衣服包裹相同的东西(condition ifTrue就像Smalltalk一样,其效果和含义与if condition)基本相同。而且,您必须对多态性使用一些流控制,除非您要将所有内容(例如整数的均匀性)包装到专用对象中。


多态性在很大程度上取决于重载,因此对于js和php之类的语言,您注定会使用这种方法。
SparK

Answers:


61

Anti-IF Campaign网站上有一些资源,例如本文

我相信这是一个程度的问题。有条件的条件并不总是坏的,但是它们可以被滥用(并且经常被滥用)。

其他想法(一天后)

重构:改进现有代码的设计是该主题(以及许多其他主题)的良好参考。它涵盖了用多态替换条件。网站上还有一个新的“用访客替换条件”

我看重简单和单一责任超过删除所有if语句。这三个目标常常是重合的。支持圈复杂度度量的静态分析工具可以快速指出带有嵌套或串行条件的代码。该if语句可以保留在重构后,但可以分解为较小的方法和/或多个类。

更新: Michael Feathers写了一篇有关无条件编程的文章。

这是一个很受欢迎的话题:菲尔·哈克(Fhil Haack)死于IF声明


4
我认为收录来自Google Tech Talks的有关此原则的视频也很重要:youtube.com/watch?v=4F72VULWFvc
War Gravy

15

经过几年的编程,我回到自己的问题上,现在我对它的上下文有了更好的了解。

桑迪·梅斯(Sandi Metz)有个很好的演讲,她将一个多毛的if语句重构为一些没有毛的东西:https : //www.youtube.com/watch?v= 8bZh5LMaSmE


7

我阅读了您链接的文章,似乎他们主要是在谈论在类中消除对条件的需要,不要与一般的所有代码相混淆。这个想法是,如果您需要检查对象的状态(使用条件)以确定其是否具有某些功能,那么实际上您有两个对象(一个对象支持该功能,另一个不支持该功能),并且应将它们定义为两个相关类。


7

我有一个同事告诉我,他曾经在一家公司工作过,该公司的政策是永远在代码中没有条件(“ if”和“ switch”语句),并且他们让代码中的所有决定都使用多态性和(我猜)其他一些面向对象原则。

我认为您的同事误解了某些内容或使用了错误的词语来解释它。而且,您无法完全避免条件语句。

有话要说:OOP中的if语句激增可能是不良编程的征兆。一些例子:

不要像老式的C风格编程那样使用if检查函数的返回值:

int ret = some_func();
if (ret != null)
   //do something

这在C代码中很典型,但是对于OOP,您应该使用异常:

try{
    do_something();
}catch(Exception e){
    e.printStackTrace(); //why I was not able to do something
    handle(e); //there is something else I could do to handle the occurred error
}

有时,如果陈述激增与不良设计有关。考虑一下Java中的以下示例:

BaseClass base;
if (base instanceof DerivedClassOneFromBase){
    DerivedClassOneFromBase d = (DerivedClassOneFromBase)base;
    d.methodOne();
}else if (base instanceof DerivedClassOneFromBase){
    DerivedClassTwoFromBase d = (DerivedClassTwoFromBase)base;
    d.methodTwo();
}

这是不良if语句的另一个示例,可能与不良设计有关。如果两个派生对象在其基类BaseClass中定义了一个通用方法,则您可以调用该方法,而不用检查其具体类型并强制转换它们:

base.commonMethod();

1
我也同意,如果应用程序内部出现不好的征兆!但是使用try catch作为流决策结构会更加糟糕,1.您以相同的方式使用if / else 2.它的速度太慢(自我体验!!!)。ps; 我并不是说您的上述代码是好是坏,因为它与任何上下文无关。
Renato Gama

@Renato-正如您所说的,这与上下文有关。Microsoft建议使用异常来报告错误/失败,而不是使用返回值。框架设计指南中有一章对此进行了介绍。
TrueWill 2011年

5
@Renato Gama:是的,例外情况稍慢一些,但关键是它们是例外情况。他们不应该控制流程决策结构,而是要处理偶然的​​错误。
Heisenbug

作为一个未成年人,例外是与OOP正交的,尽管我无法想到任何缺少它的OOP语言(除了Go,这只是一种OO。)
Kevin Kevin

5

有时,方法内部的条件是不好的,因为它们表明您只是在一个方法中执行多个函数或多个类型的方法。

如果您有一个名为Automobile的类以及诸如Car和Bike的子类,以及诸如以下的方法:

drive(Automobile a)
   if (a.isCar)
      // do stuff
   else if (a.isBike)
      // do stuff

你最有可能做错事。即使它不是基于类型的开关,也经常会出错。如果该方法根据某个变量执行多种功能,则它经常尝试做的事情不只一件事,可能应该分为多种方法。

例如:

save(Car c)
   if (c is new)
      // do some stuff
   else if (c is old)
      // do some stuff

由于它们是两个不同的功能,因此有可能分解为保存和更新。虽然这取决于。

完全禁止语句是否愚蠢,因为它们有许多有效的用例。


0

避免使用条件不一定意味着您需要通过多态或继承来完成它,例如:

您有3个不同的文件夹来存储上载的图像,上载的视频和上载的pdf

您可以将代码编写为:

uploadMedia(mediaType){
   if(mediaType == images){
     uploadTo("myProject/images");
   }else if(mediaType == videos){
     upoloadTo("myProject/videos);  
   }else if(mediaType == pdf){
     uploadTo("myProject/pdf");
  }
}

人们可能使用的另一种选择是switch-case:

uploadMedia(mediaType){
         switch(mediaType){
         case : images
         uploadTo("myProject/images");
         break;

         case : videos
         uploadTo("myProject/videos");
         break;

         case : pdf
         uploadTo("myProject/pdf");
         break;
    }
}

但是,然后您可以通过使用诸如dictionary / hashmap / json之类的东西(取决于您的工作方式)来完全避免条件语句:

例如 :

HashMap<String,String> mediaMap = new HashMap<>();

mediaMap.put("images","myProject/images");
mediaMap.put("videos","myProject/videos");
mediaMap.put("pdf","myProject/pdf");

//mediaType can be images/videos/pdf same as keys of mediaMap
uploadMedia(mediaType){
  uploadTo(mediaMap.get(mediaType));
}

这是一种伪代码,因此可能存在语法错误,但总体而言,此概念也有助于避免出现条件错误。另外,可以减少代码行。


switch语句只是用其他词(也称为“语法糖”)编写的语句,因此并不能真正回答我的问题。哈希图基本上是另一种编写switch语句的方法。如果少用编程(据我所知),更多的是避免使用(很多)if语句时(很可能)遇到的问题。它们往往会成倍增加,并使代码难以阅读。如果您使用开放/封闭原则或继承,则可以避免这种情况(当然会遇到其他问题)。
Niels Bom

我没有得到您评论这一部分哈希图基本上是另一种编写switch语句的方式。我认为它是切换大小写和if-else的替代方法,使代码更易于阅读。另外,它确实可以帮助我理解您或任何阅读此书的人是否可以告诉我使用if / else或switch / case哈希表的性能效果。这也可以帮助我理解性能瓶颈。谢谢:)
TGW

没错,使用hashmap可以替代switch-case和if-else语句。这是一种替代方法,有时可以使代码更易于阅读。如果您要寻找不是有条件的东西,这不是替代方案。只是写的不同。如果要发现一种编程解决方案对另一种编程解决方案的性能影响,则只需使用计时器即可对其进行衡量。但是,我们不要在本主题中脱节:-)
Niels Bom

谢谢,你是对的,不应该在这个话题上走题😅–
TGW
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.