#可扩展性
一个好的产品都是不断修改和重构出来的,如果我们的代码没有考虑到可扩展性,会导致修改和重构代码困难。 做好需求分析和架构设计能提高程序的可扩展性。
##需求分析
在考虑可扩展性需要从需求分析开始,需求分析这步是很多人容易忽视的,有时候需求分析做好了程序就自然有课扩展性。做需求分析是需要对需求进行陈述处理:区分做什么和怎么做,做什么往往不会变,而怎么做是可能会变的,应该把可能会变的部分独立处理。
比如产品经理告诉我们一个需求:成员列表要按姓名拼音排序(如图1-26)。
如果我们按产品经理说的做,不做任何分析的话,就可能会把数据库的排序字段存为拼音。这就没有没有可扩展性,如果产品经理以后再告诉你成员列表要把VIP的会员提到前面,这时候你只能痛苦的去修改程序。
![](https://box.kancloud.cn/2016-01-12_56951562899d3.png)
图1-26 成员列表的例子
我们先做需求陈述处理,这个需求是做什么?是做排序, 怎么做? 按姓名拼音。 要知道成员列表要排序这个需求肯定不会变, 但是按姓名拼音的排序方法是可能会变的。我们在做数据库表设计的时候大家认为排序字段应该是存拼音还是数字?
这个需求主要是要做排序,按拼音只是附加说明,这个附加说明是可能会变的,而排序永远不会变。既然是做排序,数据库字段就应该设计为数字类型的,按数字顺序排列. 再写一套算法,将拼音转换为数字,比如a转换为1,b专为2 , 新增成员时会将姓名的汉字转换为拼音,再将拼音转换为数字,然后存入排序的字段。这样产品经理告诉你要把某个人提前,只要修改某个人数据库的排序字段的数字即可,不用修改程序。
做好需求陈述处理,把做什么和怎么做分开,程序就比较有扩展性了,我们还要再写程序时注意架构设计。
## 架构设计
另外在做程序架构设计的时候我们需要学习很多编程理论,如MVC,OOP,AOP,REST,设计模式等,学习编程理论也要掌握方法,不要滥用编程理论,某个编程理论提出一定是为了解决什么问题,要分析自己的项目有没有遇到这样的问题,有这样的问题采用对应的编程理论,如果没有这样的问题硬要用这个编程理论,那就属于滥用编程理论。
大家可以问问自己是否知道MVC要解决什么问题?OOP要解决什么问题?
还要明白编程理论和编程的基础知识不一样, 基础知识不遵守就是错误,比如不按照规定的语法写程序,那运行程序的时候比如会报错,而编程理论,不遵守它是不会有错误,我们可以灵活的应用编程理论,可以对编程理论做调整来解决自己项目中的实际问题。
![](https://box.kancloud.cn/2016-01-12_56951562a978c.png)
图1-27 编程理论要解决的问题
如图1-27,我认为很多编程理论都有一个共同的目的“解耦” , 而“解耦”的大专方向是 “正交设计、类要有专职、善于用委托”,按这样去写程序,我们无意其中就用上面某些编程理论,、设计模式等。
**什么是正交设计**
![](https://box.kancloud.cn/2016-01-12_56951562bfe4b.png)
图1-28 不正交的线段
正交的概念来源于数学,如图1-28第一条线段是斜着的,和垂直的线不不是正交,而另外两条线段和垂直的线是正交,斜着的那条线段会与多条线都有交点,斜着的线有变动会影响多条线, 如果程序写成这样就会出现耦合,如果让横着的线和竖着的线都正交, 接触点会很多,从而减少耦合(如图1-29)。
![](https://box.kancloud.cn/2016-01-12_56951562cf3fe.png)
图1-29正交的线段
正交类似于地下党组织,地下党会分很多小组,小组内部的人虽然互相认识,但不能让小组之间有直接联系。即使某个小组被发现了,在严刑拷打也不能获取小组之外的人员名单。
很多编程理论都符合正交设计,比如OSI七层网络模型, MVC分层等。
![](https://box.kancloud.cn/2016-01-12_56951562dd8d0.png)
图1-30 OSI七层网络模型
如图1-30所示, 七层网络模型中,每一层只和上下相邻两层有联系,不能跨层有联系, 减少层和层之前的接触点,降低耦合。
![](https://box.kancloud.cn/2016-01-12_56951562efe22.png)
图1-31 MVC分层
如图1-31,MVC也是一样的,只是和相邻的层有联系, View不能直接调用Model。
MVC,能让网站风格的修改只固定在V层,而不影响其他层。只要做能让变化控制在固定的层里,而不影响其他层,那就是一个比较成功的架构。
做架构时要以“控制变化发送在固定的层”为目的,很多人做架构是以“减少代码工作量”为目的,想做了封装后只需要很少的代码就能开发出功能, 如果真的做到了这样,程序的灵活性反而很低, 不能应对很多需求。
**为什么类要有专职?**
类就是要做区分,把相同的归为一类,一个类要有专职,它只干一件事情。类封装得好,我们能说出这个类的职责,比如Db类就是操作数据库的,Log类就是写日志的。如果一个类没有专职,和很多功能都相关,就会有耦合。 我们切忌不要封装这样的上帝类,它万能到什么都能实现。一个类如果很杂,什么都干,没有明确的职责,那就不能称其为类,更像是一个杂种。
我们在封装类的时候,经常发现实际写的大量的代码在干和类的职责无关的事情,这时候我们应该把这样的代码独立出来。假如Log类里面有大量代码不日志无关,和读写文件有关, 我们就应该在封装一个File类来读写文件,让Log类去调用File类,写日志的方法Log::write。
不直接实现写日志,而委托File::write来完成。这就是“委托”。在封装类的时候,多用委托的方式能让类的职责更加的明确。
**为什么委托优于继承?**
继承容易出现组合爆炸的问题。
![](https://box.kancloud.cn/2016-01-12_5695156309a83.png)
图1-32 继承的组合爆炸
如图1-32,假设我们有三个类:人、鸟、鱼。人类有说话的方法,鸟类有飞的方法,鱼类有游的方法。我们如果需要封装一个类“会飞的人”,它既能说话又能飞, 那么这个类要先先继承于人类,再复制鸟类的飞的方法到新的类。“会游的人”、“会说话的鸟”等类都有这样的问题,会复制代码。功能组合的情况越多,重复代码就越多,这就是继承容易产生的“组合爆炸”的问题。
而用委托的方法,能解决组合爆炸的问题。
![](https://box.kancloud.cn/2016-01-12_569515633d9ff.png)
图1-33 使用委托解决组合爆炸的问题
如图1-33,将人类的“说”、鸟类的“飞”、鱼类的“游”都委托出去,交给新的类实现。 “会飞的人”不用继承任何基类,调用“说”和“飞”即可,同理“会说话的鸟”、“会说话的鱼”等类也是调用相应的委托类的方法即可。 这样不用复制代码即可实现功能的组合,避免组合爆炸。
做到了“正交设计、类要有专职、善于用委托”这一句,程序架构就自然做好了。