前言
小项目写多了,比如一些什么管理系统之类的,在接触大型项目的时候往往会将之前的一些面向过程的写法代入。
比如UI以及逻辑处理都放在一个类里面,UI里面加什么,我这个类里面加什么,改什么。代码改多了之后,对于新的需求,有很大概率需要重构代码,因为要处理的地方太多了。
软件讲究高内聚,低耦合,便于拓展,模块化。之前这些存在于书本上的概念,在实际开发中,是真真切切的用得上的。
对于上面的情况,这篇博客写一下处理方式。
1. 单一职责
首先是前言提到的UI以及逻辑都放在一个类里面处理这种情况。
借用书的例子做一个衍生。
目前手机拍照的效果和十年前的单反来比,效果差距还是很明显。
手机复合的功能太多,拍照,打电话,玩游戏等等,总共的空间就那么大,既要又要,所以就要对各个功能做一些取舍。常常是每一个功能都没有那么专业,仅能符合大众的日常使用需求。从照相这个功能来说,手机就不算是专业的了。
而单反相机,无论是CMOS还是性能上比手机好的太多,相机的作用在照相这个功能点上发挥到了极致。其他的点就不行了,相机打不了游戏,打不了电话…
那么在我UI和逻辑处理这个合成类中来说,它的职责就和手机一样,既要又要,导致软件在某个功能点并不拔尖。要想让这个类的作用发挥到最大,那就要使其专一化,也就是职责的单一化。
1.1 原理阐述
单一职责原则:就一个类而言,应该仅有一个引起它变化的原因
我们在做编程的时候,很自然地就会给一个类加各种各样的功能,比如我们写一个窗体应用程序,一般都会生成一个 F o r m 1 Form1 Form1这样的类,于是我们就把各种各样的代码,像某种商业运算的算法呀,像数据库访问的 S Q L SQL SQL语句呀什么的都写到这样的类当中,这就意味着,无论任何需求要来,你都需要更改这个窗体类,这其实是很糟糕的,维护麻烦,复用不可能,也缺乏灵活性。
1.2 处理方式
刚才讲了那么多,无非就是一个核心点。
一个类,做一个功能。
对于UI和一些业务代码,我们要做的就是把
U
I
UI
UI用一个类来管理操作,把业务代码用另一个类进行封装。
UI中要处理业务的地方,调用对应类的接口即可。实际处理业务不在UI类就可以了。
如果要用算法,我们可以给算法封装个类。业务处理如果要用算法,我们直接调用算法类就可以了,而不是把算法写在业务处理中。
2. 开放-封闭原则
前言部分我提到:软件讲究高内聚,低耦合,便于拓展,模块化。
我们在做任何系统的时候,都不要指望系统一开始时需求确定,就再也不会变化,这是不现实也不科学的想法,而既然需求是一定会变化的,那么如何在面对需求的变化时,设计的软件可以相对容易修改,不至于说,新需求一来,就要把整个程序推倒重来。怎样的设计才能面对需求的改变却可以保持相对稳定,从而使得系统可以在第一个版本以后不断推出新的版本呢?开放-封闭给我们答案。
2.1 原理阐释
这个原则其实是有两个特征,对于扩展是开放的(Open for extension)
,另一个是对于更改是封闭的(Closed for modification)
我的理解是,我已有的软件,如果要添加功能,那么欢迎添加功能,但是我已有的功能你不能动。添加功能就只管添加功能,如果要更改功能,那是别的类的事情。
这就要求我们设计的时候,时刻要考虑,尽量让这个类是足够好,写好了就不要去修改了,如果新需求来,我们增加一些类就完事了,原来的代码能不动则不动。
不过,我们是很难预先猜测,但我们却可以在发生小变化时,就及早去想办法应对发生更大变化的可能。也就是说,等到变化发生时立即采取行动。
2.2 举例说明
在我们最初编写代码时,假设变化不会发生。
当变化发生时,我们就创建抽象来隔离以后发生的同类变化。
比如,加法程序,我们可以很快在一个client类中就完成,此时变化还没有发生。然后再加一个减法功能,你发现,增加功能需要修改原来这个类,这就违背了‘开放-封闭原则’,于是就该考虑重构程序,增加一个抽象的运算类,通过一些面向对象的手段,如继承,多态等来隔离具体加法、减法与client耦合,需求依然可以满足,还能应对变化。这时又要再加乘除法功能,你就不需要再去更改client以及加法减法的类了,而是增加乘法和除法子类就可。
即面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。
总结
单一职责模式以及开放封闭原则,其实很大程度上不能做到完全遵守。就像我近期在公司的框架下开发新功能,有些地方不得不让一个类对某些功能进行耦合,不能完全做到单一职责。这是因为这个类在以前的代码中牵扯到很多其他代码,如果要完全实现,就必须重构,费时费力。开放封闭也是一样的道理。
这篇博客的两篇原则,总的来说是在我们开发的时候,尤其是在写新类的时候需要着重关注的。实在实现不了百分百的效果,我们亦可以通过遵守设计原则,方便后面的拓展与优化。