这是一种众所周知的重构模式,可以将条件条件替换为多态性。 如果您不熟悉该模式,可以在此处查看 。 但是,一旦该类中有多个条件检查所基于的字段,该基本解决方案便会开始崩溃。 我们将研究一些有关如何使用这些可能性的想法。

有很多方法可以解决,因此我们将从最简单到最困难的工作,始终使用简单的示例来尽可能减少混乱。 那么,最简​​单的情况是什么? 看一看:

public class ClassWithConditionals
{private boolean conditional1;private EnumeratedType conditional2;public ClassWithConditionals(boolean cond1, EnumeratedType cond2){conditional1 = cond1;conditional2 = cond2;}public void method1(){if(conditional1){//do something}else{//do something else}}public void method2(){switch(conditional2){case CASE1://do somethingbreak;case CASE2://do something elsebreak;case CASE3://do something entirely differentbreak;}}
}enum EnumeratedType
{CASE1,CASE2,CASE3
}

因此,在此示例中,我们有ClassWithConditionals在其方法中使用的两个不同字段。 在一个合适的示例中,您将假设不仅使用给定的两个方法,还可以使用更多的方法,但是对于该示例,我们仅需要两个方法。 如果每个条件只使用一种方法,那么您就不必担心,因为维护成本仍然很低。 但是,只要执行这种条件检查的方法数量增加,就应该考虑进行这种重构。

通常,如果您要遵循用多态替换条件,您将最终得到六个类来解决此问题: booleanenum每个组合都有一个。 取而代之的是,我们将使用合成。

那么,第一步是什么? 首先,我们可能应该使用enum类型。 enum可以有自己的方法,可以以允许其根据特定enum做不同事情的方式定义这些方法。 因此,让我们将enum eratedType更改如下:

enum EnumeratedType
{CASE1(){public void doSomething(){//do something}},CASE2(){public void doSomething(){//do something else}},CASE3(){public void doSomething(){//do something entirely different}};public abstract void doSomething();
}

现在, method2仅需要将自身委托给conditional2.doSomething()

现在让我们修复boolean 。 我们创建一个接口,该接口对所有非封装类(为了测试起见,可能是包)都私有,称为Conditional1 。 然后我们用TrueFalse对其进行子类化。 这是代码:

interface Conditional1
{static Conditional1 TRUE = new True();static Conditional1 FALSE = new False();void doSomething();
}class True implements Conditional1
{public void doSomething(){//do something}
}class False implements Conditional1
{public void doSomething(){//do something else}
}

我决定在接口上创建TRUEFALSE实例的原因很简单:它们都是无状态类,这意味着每个实例都具有多个实例是没有意义的。 它还允许我们像enum一样调用它们。

同样,现在主要类仅需要委托。 这是固定类现在的样子

public class ClassWithConditionals
{public static ClassWithConditionals with(boolean cond1, EnumeratedType cond2){Conditional1 conditional1;if(cond1)conditional1 = Conditional1.TRUE;elseconditional1 = Conditional1.FALSE;return new ClassWithConditionals(conditional1, cond2);}private Conditional1 conditional1;private EnumeratedType conditional2;ClassWithConditionals(Conditional1 cond1, EnumeratedType cond2){this.conditional1 = cond1;this.conditional2 = cond2;}public void method1(){conditional1.doSomething();}public void method2(){conditional2.doSomething();}
}

这里有些奇怪。 我们已经用另一个替换了一个条件。 我们的构造函数足以接受一个Conditional1 ,但是我们有一个静态工厂方法,该方法仍然采用boolean并对此进行条件检查。

考虑到从技术上讲,除非有多种方法进行检查,否则我们不会重构此代码,因此,我们进行了许多检查并将其归为一类。 同样,在工厂中通常认为有条件的还可以,将所有检查强制到一个位置,并允许多态性从那里接管。 您不必使用静态工厂方法作为工厂,但是它是最快速,最简单的实时设置方法。 允许调用新ClassWithConditionals对象的创建代码的代码仍然可以按过去的方式传递boolean的另一个好处是,它允许我们封装和隐藏基于条件的类的实现细节。 新ClassWithConditionals创建者无需担心创建Conditional1对象,甚至无需知道它的存在。

我们仍然希望构造函数接受Conditional1对象,这有两个原因:1)它将条件逻​​辑保存在工厂中,而不是构造函数中,后者是首选,并且2)它允许我们传递Conditional1对象的测试双精度。

实际上,由于第2点,我们应该经常考虑将其enum转换为更类似于Conditional1的静态实例。 这将允许您更多地使用测试双打。 它还将有助于继承或通过组合进行扩展,这将在稍后进行讨论。

可以想到很多小变化。 首先,条件boolean不需要booleanenum 。 可以有一组基于数字或其他条件的条件表达式。 通常,在这些情况下,我们用一个小的辅助方法来代替支票以使其更清晰,即if(numberOfPeople <= 3)...变成if(isACrowd(numberOfPeople))... 我们可以更进一步,并创建通过工厂创建的GroupsOfPeople层次结构。 如果工厂的值为1,则返回SinglePerson ; 给定2,则返回Company对象; 给定3或更多,它将返回一个Crowd对象。 这些对象中的每个对象都有其自己的方法,这样可以帮助减少原始类中的代码量。

另一个变化是当不同的条件字段集分层在一起时( if(condition1 && condition2)等)。 为了解决这个问题,您可以走继承路线并创建爆炸的类以覆盖所有组合。 另一个选择是用一个小的层次结构替换一个条件对象,该层次结构接受委托方法中的其他条件对象,在该方法中,它仍然具有一些条件代码,但是可读性更强。 例如,您可以将使用两个布尔值的类转换为如下形式:

public class ClassWithConditionals
{public static ClassWithConditionals with(boolean condition1, boolean condition2){Conditional1 cond1;if(condition1)cond1 = Conditional1.TRUE;elsecond1 = Conditional1.FALSE;return new ClassWithConditionals(cond1, condition2);}private Conditional1 condition1;private boolean condition2;ClassWithConditionals(Conditional1 condition1, boolean condition2){this.condition1 = condition1;this.condition2 = condition2;}public void method(){condition1.method(condition2);}
}interface Conditional1
{static Conditional1 TRUE = new True();static Conditional1 FALSE = new False();void method(boolean condition2);
}class True implements Conditional1
{public void method(boolean condition2){if(condition2){//do something}else{//do something else}}
}class False implements Conditional1
{public void method(boolean condition2){if(!condition2){//do something really different}//and do this}
}

Condition1method接受一个布尔值,然后使用它进行更多的条件处理。

另外,如果所有逻辑都允许,则可以创建一组类来替换其中一个条件,然后让其创建代码接受其他条件以决定其创建的一部分。 例如:

public class ClassWithConditionals
{public static ClassWithConditionals from(boolean condition1, boolean condition2){return new ClassWithConditionals(Conditional1.from(condition1, condition2));}private Conditional1 conditionOne;ClassWithConditionals(Conditional1 conditionOne){this.conditionOne = conditionOne;}public int method(){return conditionOne.method() * -6;}
}interface Conditional1
{static Conditional1 from(boolean condition1, boolean condition2){if(condition1)return True.with(condition2);elsereturn False.with(condition2);}int method();
}class True implements Conditional1
{public static True with(boolean condition2){if(condition2)return new True(5);elsereturn new True(13);}private int secondary;public True(int secondary){this.secondary = secondary;}public int method(){return 2 * secondary;}
}class False implements Conditional1
{public static False with(boolean condition2){if(condition2)return new False((x, y) -> x - y, 31);elsereturn new False((x, y) -> x * y, 61);}private final BinaryOperator operation;private final int secondary;public False(BinaryOperator operation, int secondary){this.operation = operation;this.secondary = secondary;}public int method(){return operation.apply(4, secondary);}
}

对于True ,第二个条件决定method计算中的次要数字。 在False ,它会执行此操作并找出要应用于计算的运算符。

我不确定是否会发生类似的事情,但是如果确实发生了,您现在知道了一种解决方法。

总的来说,这整套重构从本质上将代码从单个类更改为Facade。 它需要大量的新类,并且使您可以使用与以前的单个类几乎完全相同的方式来使用整个工具包和kaboodle,唯一真正的区别是调用静态工厂方法而不是构造函数。

这不是特别重要; 我只是想向您指出。

希望您不必担心继承或“通过合成扩展”此类。 但是您可能必须这样做。

如果您要编写的扩展仅真正改变了条件对象的功能,则可以简单地编写一个新的Factory,为构造函数提供一组新的条件对象。 例如,您可以将此静态工厂方法添加到ClassWithConditionals的最新版本中:

public static ClassWithConditionals different(int value)
{return new ClassWithConditionals(new SimpleConditional1(value));
}

SimpleConditional1看起来像这样

class SimpleConditional1 implements Conditional1
{private final int value;public SimpleConditional1(int value){this.value = value;}public int method(){return value;}
}

除此之外,您还可以提供原始需要的任何条件对象,并覆盖您需要覆盖的所有方法。

因此,这就是我用一个更多的OO选项替换多个条件的结果。 您还有其他方法可以做到吗? 您是否有一个无法奏效的示例,您希望我对此加以抨击? 让我知道,我会看看可以做什么。

谢谢阅读。

翻译自: https://www.javacodegeeks.com/2015/01/replacing-multiple-conditionals-with-polymorphism-and-composition.html

用多态和组合替换多个条件相关推荐

  1. js 使用多态替换条件语句_用多态和组成替换多个条件

    js 使用多态替换条件语句 用多态替换条件语句是一种众所周知的重构模式. 如果您不熟悉该模式,可以在此处查看 . 但是,一旦类中有多个条件检查所基于的字段,该基本解决方案便会开始崩溃. 我们将研究一些 ...

  2. 白话Elasticsearch03- 结构化搜索之基于bool组合多个filter条件来搜索数据

    文章目录 概述 数据 小示例 搜索发帖日期为2017-01-01,或者帖子ID为XHDK-A-1293-#fJ3的帖子,同时要求帖子的发帖日期绝对不为2017-01-02 搜索帖子ID为XHDK-A- ...

  3. 开关插座手册_如何用开关/插座组合替换电灯开关

    开关插座手册 If you're running out of outlets to plug things into, installing a switch/outlet combo is a g ...

  4. php正则替换%3cbr%3e_PHP preg_replace() 正则替换所有符合条件的字符串

    需要我们用程序处理的数据并不总是预先以数据库思维设计的,或者说是无法用数据库的结构去存储的. 比如模版引擎解析模版.垃圾敏感信息过滤等等. 一般这种情况,我们用正则按我们的规则去匹配preg_matc ...

  5. C++ 笔记(32)— 预处理、文件包含include、宏替换define、条件包含ifndef、define

    C/C++预处理器在源代码编译之前对其进行一些文本性质的操作. 它的主要任务包括删除注释 . 插入 #include 指令包含的文件的内容 . 定义和替换由 #defme 指令定义的符号以及确定代码的 ...

  6. lincx Shell脚本编程之字符串的截取,替换,按条件掐头去尾

    字符串处理 子串截取 三种方法 方法一:${变量名:起始位置:长度} 方法二:expr subsrt "$变量名" 起始位置 长度 方法三:echo $变量名 | cut -b 起 ...

  7. preg_replace() 正则替换所有符合条件的字符串

    PHP preg_replace() 正则替换,与Javascript 正则替换不同,PHP preg_replace() 默认就是替换所有符号匹配条件的元素 需要我们用程序处理的数据并不总是预先以数 ...

  8. Python笔记(二十)_多态、组合

    多态 对于一个函数中的变量,我们只需要知道它这个变量是什么类,无需确切地知道它的子类型,就可以放心地调用类的方法,而具体调用的这个方法是作用在父类对象还是子类对象上,由运行时该对象的确切类型决定,这就 ...

  9. Linux之find + sed组合替换命令

    一.操作替换文件 1.将111.txt中所有的1111替换为linux字符串 # sed 's/1111/linux/' 111.txt2.仅打印被替换行 # sed -n 's/1111/linux ...

最新文章

  1. jquery格式化时间
  2. JAVA屏幕截图与水印添加程序-HEHEHEScreenshot
  3. ai带来的革命_Covid-19将加速AI医疗保健革命
  4. 成年人夜生活的自救! 华熙LIVE·五棵松放大招
  5. 图片背景处理技巧快来学学
  6. Hadoop发展编年史
  7. 吴伯凡-认知方法论-原始舒适区=0认知
  8. java web运行网址_猫哥带你去战斗——Java Web开发——网页篇[0]——第一个网页,第一次访问...
  9. 包包各部位名称图解_七大箱包主要部位的结构专业术语和特性
  10. 计算机撤销英语,正在撤销对计算机所做的更改要等多久
  11. Linux用户管理安全宝典:密码防破解与帐号文件保护
  12. 计算机软件分类系统软件和,计算机化系统软件分类和验证
  13. 苹果照片未删却不见了_删掉的照片怎样找回?教你打开手机这个功能,可以立马恢复...
  14. vue动态设置路由重定向
  15. react前端显示图片_在react中怎么动态渲染图片?
  16. 微信小程序云开发学习
  17. 武汉星起航——卖家关注!亚马逊为促进行业发展,近期有了新动作
  18. 三洋服务器Q系列报警代码,三洋伺服故障报警维修
  19. #.数学函数3D图的绘制
  20. 人体关键点检测的评价度量PCK, PCKh, PDJ

热门文章

  1. hibernate注解实体类(Emp.java)
  2. 当当网新用户注册界面——界面源码
  3. Mysql对字符串去掉前后空格(trim)或者指定字符
  4. 配置struts.xml时extends=struts-default会报错,原因和解决
  5. Android RaingBar评分条的使用
  6. python爬新闻并保存csv_用python爬取内容怎么存入 csv 文件中
  7. centos8上docker tomcat容器访问报404解决方法
  8. JDBC的SQL注入漏洞
  9. selenium自动化测试_您如何使用Selenium来计算自动化测试的投资回报率?
  10. json解析对象时指定解析_不解析,使用解析对象