软件开发中,“配置开关”根本算不上是一个“新”概念。无论是通过启动时加载配置,还是应用程序运行时动态刷新配置,都可以用来决定或改变应用程序的某种具体行为。比如:
- 通过配置文件,使用应用程序连接不同的数据库
- 通过模板配置,达到应用程序“换肤”的目的
- 通过系统管理员的配置,使不同的用户具有不同的权限
所有这些“开关”都被认为是理所当然的,因为用户的需求就是这样的,“我需要通过xx开关来控制yy”。因此,做为交付团队,也会老老实实地把这些需求实现了。
然而,现在的很多团队,为了能够尽早发布软件,使用主干开发方式,并在程序中加入了另外一种“对用户透明”的开关。这种开关通常有两种使用场景是:
- 某些高级(或创新)的特性已经开发完毕,但业务人员根据市场情况,认为不需要投放市场。
- 市场需要某些已开发完成的特性,但还有一些特性尚未完成,仍旧处于开发中。
无论哪种情况,都是要求将某些特性进行隐藏后再发布。这种开关常常被认为是不可取的,但现在很多需要“持续交付”的公司都在使用。
(有的同学会说:“这完全可以通过按特性拉分支的方式来解决”。关于这个问题,我会在下一篇中讨论,本次仅限于讨论在使用“主干开发”的情况下,如何正确使用开关。)
开关最容易的地方就是应用程序的用户界面。当为已上线的应用程序重新设计了全新交互界面后,团队无法在一次性在同一发布周期中将其修改完成时,可以 先保留原有页面不变,同时每次仅替换少数的新页面。令所有未开发完成的页面对用户不可见,例如:原有页面的URL是 http://xx.xx.xx.xx/create_user,那么对应的新页面使用http://xx.xx.xx.xx/new_UI /create_user(要对后台逻辑的修改应能够同时响应新旧两个页面的请求)。当新页面完成后,将原来创建用户的页面链接重新指向新页面就可以了。 这样,就能在不破坏原有功能的前提下,做到持续发布。
开关实现形式
另外,对于那些无法在一个发布周期内实现的新功能,我们可以使用下面形式的“开关”:
- 配置文件,通常在启动时加载
<features>
<feature name="hq.remote_panel_load" />
<feature name="el.enable_asset_library" />
...etc...
</features>
- 在代码中实现
public function isFeatureOn(featureName:String):Boolean {
var nodes:XMLList = xml.features.feature.
(@name==featureName);
return null != nodes && 0 < nodes.length();
}
- 设计成API
${core-url}/accounts/featureBits?userUid=&orgUid=&
而在编码中,可以使用不同的实现方式:
- 最简单的检查
if( registry.config.isFeatureOn( featureName ) ) {
// new implementation ...
} else {
// the old way ...
- 使用策略模式
if( registry.config.isFeatureOn("ct.analyzer.v2") ) {
service.analyzer = new Analyzer2();
} else {
service.analyzer = new Analyzer();
}
- 使用工厂模式
public function createMainDisplay():DisplayObject {
if( registry.config.isFeatureOn( "service.panel.v2" ) ) {
return new panel2(); // which extends panel
} else {
return new panel(); // which extends DisplayObject
}
}
- 使用责任链方式
if( registry.config.isFeatureOn("hq.trickle_reporting") ) {
userActionLogger = userActionLogger.setNext(
new TrickleReportNotifier( .. )
);
}
注意事项:
- 提前评估是否需要开关,需要的话提前设计,不应该随意在代码中添加。
- 定义一个开关命名规则,并在团队中达成共识。
- 尽可能在设计时考虑在哪些层次上加开关。
“开关”在使用中的反模式:
- 代码中到处都是开关,个数太多;
- 在开关完成其使命后,未及时清理,使代码变烂。
现在,很多公司的产品都使用这种开关方式。进一步阅读,请参见:
- Martin Folwer: Feature Toggle
- Flickr: Flipping out
- Etsy.com: Go or No-Go
- Gflag: 针对C/C++的开源开关工具,由Google贡献
代码示例来自于:Erik Sowa,"Feature Bits" on slideshare.net
更多关于"持续交付"的内容请见http://www.continuousdelivery.info。
本文版权归作者乔梁所有,转载请包含作者签名和出处,不得用于商业用途,作者将保留“追究法律责任”的权利!