一个系统通过配置文件或者数据库设置几个到几十个开关通常是没有问题,但是如果这个系统拥有上千个开关并且他们之间可能相互依赖,那对于开发、测试人员来说绝对是一个灾难。这样的灾难恰好出现在我之前维护的系统里面,代码大约有700万行代码,代码里面配置了几千个开关!系统很小的时候,能够应付得过去,这样做没有问题。当客户变多的时候,她们向需求人员提出了越来越多定制化要求,通常这些变化是无法拒绝的。况且这对公司也是有好处的,毕竟每一个变化是要算算钱的;另外通过不同需求可以攒出一个产品系统功能更加丰富,销售可以向客户表达我们的系统可以顶你们10个子系统。
实际上我们的系统在面向不同客户是想了一些办法的,比如不同的客户通过oracle数据库里面配置不同schema,不同的schema里面表结构相同,库表里面有一个开关配置表,用户在里面定义不同的开关,一套程序读取不同的开关。为了便于升级维护,代码是一套代码,可怜的程序员加上一个又一个if...else...代码。这样做会造成如下问题:
- 假定客户A需要开关1打开,客户B开关1默认关闭,开发、测试人员需要考虑两种情况。系统升级后,客户B则被强制升级。万一没有详尽的测试出现bug,客户B无辜受到影响。
- 不同开关之间有相互叠加,测试数量呈现指数级增加。对开发人员也是一场噩梦,代码复杂程度大大增加。
- 增大了测试成本,因为不同开关组合的情况在实际使用中并不存在,但是为了保证完备性,测试人员需要测试实际上并不使用场景,万一哪天要使用某个条件组合呢。
- 增加新客户的时候,运维人员需要大量配置不同开关。谁知道这么多开关代表什么含义?这么多开关组合在一起谁能保证他们能够完全正常工作?所以上线前,测试又需要再测试一遍。
- 代码耦合度高
所以加一个看似简单的需求,实际开发测试量非常巨大,系统到后来越来越难以维护。
从技术层面思考如下:
- 代码层面的相互隔离,这样即便客户A出现了问题,问题也将局限在客户A这个层面。
- 去掉if...else...代码,将开关抽象出来用策略模式代替。
代码层面如何相互隔离比较有意思,实际要求不同客户绑定不同的服务实例。
一个简单想法是不同的客户部署不同版本的代码,但这样势必增加不同版本的代码维护量,并且数据库库表结构不同。如果某个开关只有客户A使用,我们完全可以给他单独部署一个。
不同的开关加载对应的实现类,把创建开关的权利交给容器,这其实与spring的思想不谋而合。当实际也是很复杂的事情,在某些情况下甚至不可能。但不管怎样,在系统变得越来越复杂的情况下,我们应该抛弃if...else...的写法。
原文地址:https://www.cnblogs.com/pmh905001/p/12325720.html