特性开关之策略模式

响应领导号召,我开始研究特性开关。发现在实现特性开关的方式上大有文章。使用不当会引入一些问题,而使用得到则可大大减少问题。

实现特性切换很简单,使用if…else方式

实现特性切换很简单嘛!就if….else …嘛,比如下面这个

特性开关之策略模式

真的就这么简单吗?

if…else…方式存在的问题

其实真的就这么简单,使用if..else就实现了特性切换。但是if..else…这种方式会带来两个问题。

容易造成逻辑混淆,降低可读性

添加特性开关前:

特性开关之策略模式

添加特性开关后:

特性开关之策略模式

可以看到,新加入的条件式特性开关 brandA.isActive() 与原有业务判定逻辑 currentBrand == brashA 十分相似,很难区分,在使用过程中更是特别容易混淆。更糟的是,随着项目的不断深入,越来越多的条件式特性开关会被放置在代码中,代码中会充满“坏味道 “,使得混淆的情况进一步恶化!

不方便移除开关,移除开关必须从业务方法中移除代码

特性开关之策略模式

这么说来使用if…else..方式实现特性开关是万恶的?

if…else…方式是不能完全杜绝的,但很多时候我们可以使用策略模式这种更优的方案.

if…else…本身没有错,错在与原有业务逻辑的层次不一样,错在特性开关有移除的管理需求。if..else…的万能性又决定一些情况下你还不能完全消灭它,有它的使用场景,关于什么时候适合使用if…else 下面会有详细说明。针对if..else…带来的问题,很多时候我们可以用策略模式来解决。将if里的内容和else里的内容抽象成一种策略和两个实现,然后通过接口层面的开关决定使用哪个实现。

什么是策略模式

策略模式是指对一系列的算法定义,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
特性开关之策略模式

使用策略模式带来什么收益

策略模式的优点有:策略模式提供了管理相关的算法族的办法、策略模式提供了可以替换继承关系的办法、使用策略模式可以避免使用多重条件转移语句。具体到特性开关的使用上,用策略模式带来的收益就是,避免if…else…造成的逻辑混乱可读性差等问题,实现了移除开关而无需修改业务方法的目的。

如何使用FF4J实现策略模式

FF4J 实现了AOP-Driven ,我们可以用它来实现策略模式,如下所示
特性开关之策略模式
这里我们在接口上添加了开关,当开关打开时会使用 HelloEN 策略,当开关关闭时会切换使用 HelloFR 策略。

具体怎么做?参考下面这个例子

2017年年末我们调研了urule,准备用 urule 替代付费产品益博睿,替代需要个过程,不能草率,需要做A/B Testing,就是需要在线上同时跑 urule 和 益博睿,来比较它们之间的异同,确认稳妥才能全面切换到 urule。这个场景可以用特性开关来实现。

if..else..方式实现

特性开关之策略模式

策略模式方式实现

特性开关之策略模式

上面我们把if和else里的内容抽象成了一个RuleEngineStrategy策略接口,然后if里的内容对应UruleEngineStrategy实现,else里的内容对应YiBoRuiStrategy实现。大家可以看到,我们的业务方法businessMethod变得很简洁了。我们只需要面向策略接口调用接口的makeDecision方法,具体使用哪个实现则是由ruleSwitch这把开关决定,当开关打开时会使用UruleEngineStrategy或则使用YiBoRuiStrategy! 大家再想想使用这种策略模式,对以后清除开关有什么好处?很明显吧! 用这种方式我们的业务方法一句代码都不用改动,只需要将策略接口里的开关哪行注解移除就好了!简直完美!

什么时候不使用策略而是直接使用 if…else…?
有 if 没有 else 的情况

比如说你新增加了一个特性,只在开关打开时能访问到这个特性

if 里的内容与 else 里的内容不在一个层次上的时候

如果说 if 里的内容与 else里的内不在一个层次,那么就没办法抽象成一种策略。

特性开关之策略模式

上面这种 if 里要做的事 和 else 里要做的事就不在一个层次,没办法抽象成一种策略。