这几个原则看的真的是枯燥无味,单一职责原则跟开放-封闭原则可以很好的去理解,依赖倒转,是真的难理解,可能是还没接触后面的设计模式吧,毕竟刚来京都不久,涉世未深,这里记录一下,等学完23种设计模式后再来重构这篇文章。
单一职责原则
我想大家都听过这么一句话,术业有专攻。这句话呢,非常好理解:就是每个人的特长都是不一样。比如刚入职场的小林,它对后端技术栈有清晰的认识,目前也在努力的学习设计模式,但是小林写前端就不行了,Vue一无所知。
假设时间回到小林上大学的时候,小林喜欢编程,一会学后端,一会学前端,那么现在的小林可能就找不到工作了。找不到的原因是什么呢?原因就是知识面广,但是都不深,让写接口吧不会写,让写前端页面吧,又写的稀烂。
从小林的例子结合工作中角色划分来说,在工作中我们有产品,有技术经理,有后端,有前端,有运维等等。为什么要分这么多角色呢?这里小林有要给大家来一句名言:存在就是道理。也就是说既然之前这么分就有它的道理,说明这么分,团队才能发挥最大的作用!而这些人各有各的职责。比如产品职责是提需求,技术经理协调大家工作并提供技术支持,后端写接口,前端写页面。
假设小林经过修炼,后端非常的牛,前端也可以快速上手。公司最近活比较多,前端组组长让小林帮忙写页面,小林不得已接收了安排,这样小林就一分为二,0.5个在后端组,0.5个在前端组。好事多磨啊,两个组长同时给小林新需求,而且都非常急,第二天就要验收。
小林非常的无奈,心里想:我的职责是写后端,虽然我会写前端,但是写不快啊,让我在两个项目组里,那么前端组跟后端组不就因为我“耦合”了吗?
大家可以看看专栏的面向对象文章与策略模式文章的第一版代码,都是这种问题,所有的代码都写在一个类里面。这就意味着,无论任何需求要来,你都需要更改这个类,因为你就这一个类,这其实是很糟糕的,维护麻烦,复用不可能,也缺乏灵活性。”
总结:
-
单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。
-
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制在这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。
-
当然,软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。其实要去判断是否应该分离出类来,也不难,那就是如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,就应该考虑类的职责分离。
开放-封闭原则
在讲这个原则之前,送给大家程杰老师的一句话:希望读者您能对痛苦关闭,对快乐开放。
其实开闭原则对于很多人来说是经常听到的,小林也是无数次在各大博主的博客看到,在各大培训平台的视频听过,解释大概都是这样的:开闭原则就是对扩展开放,对修改封闭。
小林没参加工作之前,一直有这么个理想:我以后写的代码一定是不能改动的,这样才符合开闭原则中的闭。然而工作后小林却发现,尽管是大牛也不能做到完全的封闭。原因是什么呢?不断更改的需求,客户的需求是一直在变化的。所以,我们能做的就是修改时,改动尽可能的小。
开放这个词语一到小林这里,哎,浮想联翩,这到底是个贬义词还是褒义词。好吧,小林承认,在男性同胞眼里,暂时都认为是褒义词吧。谁不喜欢开放的女孩子,对吧。
路好像走路远了,我们回来,在开发中,开放指的就是功能的扩展是开放的,也就是说扩展功能时,增加类是正常的,并且扩展与修改也是相辅相成的。比如说策略模式中的代码,我要加一个满100送10积分的功能,该如何做?做法大概是这样:新建一个送积分类实现策略接口,然后去Context类中的switch中添加一个“满100送10积分”的分支。
上述情况下:对扩展开放,就是新建了一个积分类,不改动其他的满减类,打折类,非常的奈斯。对修改关闭上方说到了,这是短时的关闭,只要有新需求,关了也得打开重新做,我认为衡量封闭原则就是看改动的量是否大。改动越小越好!
总结:
-
开放-关闭原则是说软件实体(类,模块,函数等等)应该可以扩展,但是不可以修改。
-
我们在做任何系统的时候,都不要指望系统一开始时需求确定,就再也不会变化,这是不现实也不科学的想法,而既然需求是一定会变化的,那么如何在面对需求的变化时,设计的软件可以相对容易修改,不至于说,新需求一来,就要把整个程序推倒重来。怎样的设计才能面对需求的改变却可以保持相对稳定,从而使得系统可以在第一个版本以后不断推出新的版本呢?开放-封闭给我们答案。
-
无论模块是多么的‘封闭’,都会存在一些无法对之封闭的变化。既然不可能完全封闭,设计人员必须对于他设计的模块应该对哪种变化封闭做出选择。他必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化.
-
“开放-封闭原则是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护、可扩展、可复用、灵活性好。开发人员应该仅对程序中呈现出频繁变化的那些部分做出抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。切记,切记。”
依赖倒转原则
说这个原则之前我们先简单说说里氏代换原则,它的概念是子类型必须能够替换掉它们的父类型。很难懂,程杰老师的翻译是:一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象。也就是说,在软件里面,把父类都换成它的子类,程序行为没有变化。简单的说,子类型必须能够替换掉他们的父类型。
依赖倒转原则,原话解释是抽象不应该依赖细节,细节应该依赖于抽象,说简单点,就是要针对接口编程,不要对实现编程,比如我们的电脑,无论主板、CPU、内存、硬盘都是在针对接口设计的,如果针对实现来设计,内存就要对应到具体的某个品牌的主板,那就会出现换内存需要把主板也换了的尴尬。所以说,PC电脑硬件的发展,和面向对象思想发展是完全类似的。这也说明世间万物都是遵循某种类似的规律,谁先把握了这些规律,谁就最早成为了强者。
为什么要叫倒转呢?
面向过程的开发时,为了使得常用代码可以复用,一般都会把这些常用代码写成许许多多函数的程序库,这样我们在做新项目时,去调用这些低层的函数就可以了。比如我们做的项目大多要访问数据库,所以我们就把访问数据库的代码写成了函数,每次做新项目时就去调用这些函数。这也就叫做高层模块依赖低层模块。我们要做新项目时,发现业务逻辑的高层模块都是一样的,但客户却希望使用不同的数据库或存储信息方式,这时就出现麻烦了。我们希望能再次利用这些高层模块,但高层模块都是与低层的访问数据库绑定在一起的,没办法复用这些高层模块,这就非常糟糕了。就像刚才说的,PC里如果CPU、内存、硬盘都需要依赖具体的主板,主板一坏,所有的部件就都没用了,这显然不合理。反过来,如果内存坏了,也不应该造成其他部件不能用才对。而如果不管高层模块还是低层模块,它们都依赖于抽象,具体一点就是接口或抽象类,只要接口是稳定的,那么任何一个的更改都不用担心其他受到影响,这就使得无论高层模块还是低层模块都可以很容易地被复用。这才是最好的办法。