设计模式原则,其实就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础,即设计模式为什么这样设计的依据。
1)单一职责原则
定义:一个类,只有一个引起它变化的原因。即:应该只有一个职责。
如果一个类有一个以上的职责,这些职责就耦合在了一起,就会出现当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和界面的分离。需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都需要遵循这一重要原则。
问题由来:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。
解决方法:分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。
思考:单一职责是什么?是只负责一种责任吗?类比到类的话,是只实现一种功能吗?百度了一下职责的定义:“所负责的范围和承担的一系列工作任务,以及完成这些工作任务所需承担的相应责任”,哦哦,那我可以理解成老师有育人的职责,然后育人又可以分为一系列任务~~授课、解惑等,然后作为一个老师,不能有食堂阿姨的职责,如果有的话,就会造成关系复杂化,如果食堂提早饭点时间,那么就有可能会对老师的正常上课的时间造成影响,所以就要有两种身份,老师和食堂阿姨,分别负责自己的职责,互不干扰。这只是我自己的思考,是否正确我也不清楚,哈哈。
2)接口隔离原则
定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
问题由来:如果接口有五个方法,分别为m1、m2、m3、m4、m5,类A通过接口I依赖于类B的m1和m2方法,类C通过接口I依赖于类D的m3、m4和m5方法,接口I对于类A和类C来说不是最小接口,因为类B和类D要去实现他们根本不需要的方法 。
解决方法:1、 使用委托分离接口。2、 使用多重继承分离接口。3、将臃肿的接口I拆分为独立的两个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。
思考:这个原则还挺好理解的,只要自己想要的,其它多余的我一概不要,恩,没毛病,做人不能太贪心。
3)依赖倒转(倒置)原则
定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。中心思想是面向接口编程 。
问题由来:类A直接依赖类B,假如要将类A改为补在依赖类B,改为依赖C,则必须通过修改类A的代码来实现。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;修改类A,可能会给程序带来不必要的风险。
解决方法:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。
思考:这个原则让我想起了,List list = new ArrayList();和ArrayList list = new ArrayList();的区别,方法实现依赖于抽象,不依赖于具体的实现类,假如后期要把依赖改成LinkedList的话,直接把new ArrayList()改成new LinkedList()就可以了,如果直接依赖于实现类的话,因为它们中具体的方法有些区别,一修改的话,就需要改动程序。
4)里氏替换原则
定义:任何基类可以出现的地方,子类一定可以出现,也就是子类必须能够替换掉它们的父类。
问题由来:有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。
解决方法:类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法
思考:这原则怎么感觉怪难理解的?可以这样理解嘛,假如有鸟这个父类,然后它有这个会飞方法,然后它的子类猫头鹰、麻雀都继承了这个方法,都会飞,但是有个特例,企鹅,企鹅是鸟,它不会飞,它要重写父类的会飞方法,如果是这样子的话,当外部程序要调用父类的时候,都得判断一下是不是企鹅,不然程序会有bug,这样子的话就不满足里氏替换原则,同时也不满足开闭原则,因为需要修改外部程序进行判断。
5)开闭原则
定义:软件实体应当对扩展开放,对修改关闭。这句话说得有点专业,更通俗一点讲,也就是:软件系统中包含的各种组件,例如类以及功能等,应该在不修改现有代码的基础上,去扩展新功能。开闭原则中原有“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于代码的修改是封闭的,即不应该修改原有的代码。
问题由来:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。这就对我们的整个系统的影响特别大,这也充分展现出了系统的耦合性如果太高,会大大的增加后期的扩展,维护。为了解决这个问题,伟人们总结出了开闭原则。解决开闭原则的根本其实还是在解耦合。所以,我们面向对象的开发,我们最根本的任务就是解耦合。
解决方法:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
思考:这个就让我想起了“牵一发而动全身”这个词语,可能你对源代码的一点改动,就有可能造成程序的炸裂,估计程序员们都深有体会。
6)迪米特法则(最少知道原则)
定义:迪米特法则又叫最少知道原则,即:一个对象应该对其他对象保持最少的了解。如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。简单定义为只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。
问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
解决方法:尽量降低类与类之间的耦合。 自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。
思考:不知道可不可以理解成类似于面向对象编程的封装性。封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式,好吧这个原则有点难理解。
7)合成复用(合成/聚合)原则
定义:也有人叫做合成/聚合原则,及尽量使用合成/聚合,尽量不要使用类继承。换句话说,就是能用合成/聚合的地方,绝不用继承。
问题由来:对象的继承关系在编译时就定义好了,所以无法在运行时改变从父类继承的子类的实现;子类的实现和它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化;当你复用子类的时候,如果继承下来的实现不适合解决新的问题,则父类必须重写或者被其它更适合的类所替换,这种依赖关系限制了灵活性,并最终限制了复用性。
思考:好像我写代码挺少用到继承的,看来这个原则与我有缘。