【设计模式】三大原则 单一职责原则、开放-封闭原则、依赖倒转原则
系列文章目录
文章目录
- 系列文章目录
- 一、单一职责原则
- 方块游戏的设计
- 二、开放-封闭原则
- 原则介绍
- 何时应对变化
- 三、依赖倒转原则
- 依赖倒转原则介绍
- 里氏代换原则
- 总结
一、单一职责原则
单一职责原则,听字面意思,就是说功能要单一,他的准确解释是,就一个类而言,应该仅有一个引起他变化的原因,我们在做编程的时候,很自然的就会给一个类加各种各样的功能,比如我们写一个窗体应用程序,一般都会生成一个Formal这样的类,于是我们就把各种各样的代码,像某种商业运算的算法,或者数据库访问的SQL语句什么的都写进到这样的类中,这就意味着,无论任何需求要来,你都需要修改这个窗体类,这其实是很糟糕的,维护麻烦,复用不可能,也缺乏灵活性。
单一职责原则:就一个类而言,应该仅有一个引起他变化的原因。
方块游戏的设计
我们这里通过手机里的俄罗斯方块这款游戏为例,开发这个小游戏,我们应该怎么做呢?
我们可以先建立一个窗体,然后加一个用于游戏框的控件,一个按钮Button来控制‘开始’,最后计时器控制用于分时动画的编程,写代码当然就是编写计时器时间来绘出和删除方块,并做出堆积和消层的判断,再编写控件的键盘事件,如左箭头左移,右箭头右移等。这里我们思考,如果把所有的代码都写在了窗体这个java类里面,其实这样是不合理的,因为如果这样的话,如果要开发其他版本的俄罗斯方块,或者是Web版或者Windows窗台版的俄罗斯方块,那现在这个代码其实是不好复用的!
但这当中,有些东西是始终没变的,比如说下落、旋转、碰撞判断、移动堆积这些游戏逻辑。这些都是和游戏有关的逻辑,和界面如何表示没有什么关系,为什么要写在一个类里面呢?如果一个类承担的责任过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成的其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。 所以,我们应该找出那些是界面,那些是游戏逻辑,然后进行分离。
软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。 其实要去判断是否应该分离出来类,也不难,那就是如果你能够想到多余一个的动机去改变一个类,那么这个类就具有多于一个的职责 。
二、开放-封闭原则
原则介绍
开放-封闭原则,是说软件实体(类、模块、函数等)应该可以扩展,但是不可修改 。
这个原则其实是有两个特征,一个是说‘对于扩展是开放的’ , 另一个是说‘对于修改是封闭的’ 。
我们在做任何系统的时候都不要指望系统一开始时需求绑定,就再也不会变化,这是不现实也不科学的想法,需求是一直变化的,那么如何在面对需求的变化时,设计的软件可以相对容易修改,不至于说,新需求依赖,就要把整个程序推倒重来。怎样的设计才能面对需求的改变却可以保持相对稳定,从而使得系统可以在第一个版本以后不断推出新的版本呢? 开放-封闭给了我们答案。设计软件要容易维护又不容易出问题的最好的办法就是多扩展,少修改。
何时应对变化
开放–封闭原则的意思就是说,你设计的时候,时刻要考虑,尽量让这个类足够好,写好了就不要去修改了,如果新需求来,我们增加一些类就完事了,原有的代码能不动则不动,但是,绝对的对修改关闭是不可能的,无论模块是多么的‘封闭’,都会存在一些无法对之封闭的变化。既然不可能完全封闭,设计人员必须对于他设计的模块应对出哪种变化封闭做出选择。他必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化。
但是,事先猜测是很难做到的,但我们却可以在发生小变化时,就及早去想办法,应对发生更大变化的可能,也就是说,等到变化发生时立即采取行动,在我们最初编写代码时,假设变化不会发生,当变化发生时,我们就创建抽象来隔离以后发生的同类变化
我们在简单工厂模式中,一开始只有一个加法程序,后面我们需要增加一个减法功能,我们发现,增加功能需要修改原来这个类,这就违背了今天所讲到的‘开放-封闭’ 原则,于是你就该考虑重构程序,增加一个抽象的运算类,通过一些面向对象的手段,如继承、多态等来隔离具体加法、减法与client耦合,需求依然可以满足,还能应对变化,这时我们再加乘除法功能,就不需要再去更改client类了二十增加乘法和除法的字类就可,即面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码,这就是开放–封闭原则 的精神所在。
当然,并不是什么时候应对变化都是容易的,我们希望的是在开发工作展开不久就知道可能发生的变化,查明可能发生的变化所等待的时间越长,要创建正确的抽象就越困难。
开放封闭原则是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护、可扩展、可复用、灵活性好。开发人员应该仅对程序中呈现出频繁变化的那些部分做出抽象,然而对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。
三、依赖倒转原则
依赖倒转原则介绍
依赖倒转原则,原话解释是抽象不应该依赖细节,细节应该依赖于抽象,说简单点,就是要针对接口编程,不要对实现编程
依赖倒转原则
(1)高层模块不应该依赖低层模块。两个都应该依赖抽象。
(2)抽象不应该依赖细节。细节应该依赖抽象。
那么为什么要叫倒转呢?
在面向过程开发时,为了使得常用代码可以复用,一般都会把这些常用代码写成许许多多函数的程序库,这样我们在做新项目时,去调用这些低层的函数就可以了。比如我们做的项目大多要访问数据库,所以我们就把访问数据库的代码写成了函数,每次做新项目时就去调用这些函数。这也就叫做高层模块依赖低层模块。
其实问题也就出在这里,我们做新项目时,返现业务逻辑的高层模块都是一样的,但客户却希望使用不同的数据库或者储存信息方式,这时就出现麻烦了。我们希望能再次利用这些高层模块,但高层模块都是与低层的访问数据库绑定在一起的,没办法复用这些高层模块。这里我们以电脑举例,电脑里如果CPU、内存、硬盘都需要依赖具体的主板,主板一坏,所有的部件就都没用了,这显然不合理,反过来,如果内存坏了,也不应该造成其他部件不能用才对。而如果不管高层模块还是低层模块,他们都依赖于抽象,具体一点就是接口和抽象类,只要接口是稳定的,那么任何一个更改都不用担心其他受到影响,这就使得无论高层模块还是底层模块都可以很容易地被复用。
如果这里还不是很理解,也是很正常的,因为还有一个设计原则,让我们产生困惑。这个原则就叫里氏代换原则
里氏代换原则
里氏代换原则: 一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,在软件里面,把父类都替换成它的子类,程序的行为没有变化
里氏代换原则: 子类型必须能够替换掉它们的父类型。
这里貌似是继承需要理解的概念,子类继承了父类,所以子类可以以父类的身份出现。
小问题:如果在面向对象设计时,一个是鸟类,一个是企鹅类,如果鸟是可以飞的,企鹅不会飞,那么企鹅是鸟吗?企鹅可以继承鸟这个类吗?
其实这里是不可以的,我们讨论的是面向对象设计时,也就是说,子类拥有父类所有非private的行为和属性。鸟会飞,但是企鹅不会飞。所以这里企鹅不能以父类–鸟的身份出现,因为前提说所有鸟都可以飞,而企鹅飞不了,所以企鹅不可以继承鸟类。
所以,只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。
正是由于子类型的可替换性才使得使用父类类型的模块在无须修改的情况下就可以扩展 。
依赖倒转其实可以说是面向对象设计的表示,用哪种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之那就是过程化的设计了
总结
以上就是本文全部内容,本文主要向大家介绍了设计模式中的三大原则-----单一职责原则、开放-封闭原则、依赖倒转原则。感谢各位能够看到最后,如有问题,欢迎各位大佬在评论区指正,希望大家可以有所收获!创作不易,希望大家多多支持!
最后,大家再见!祝好!我们下期见!