设计模式
设计原则
开闭原则
软件实体应当对扩展开放,对修改关闭
这就是对开闭原则的经典定义
开闭原则是面向对象设计的终极目标,它使软件实体拥有一定的适应性灵活性的同事具备稳定性和延续性
- 软件测试时只需要对扩展的代码进行测试就可以了,因为原油的测试代码仍能够正常运行
- 粒度越小,被复用的可能性就越大;在面向对象的程序设计中,根据原子和抽象编程,可以提高代码的可复用性
- 遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护
里氏替换原则
继承必须确保超类所拥有的的性质在子类中任然成立
作用如下
- 里氏替换原则是实现开闭原则的重要方式之一。
- 他克服了集成中重写父类造成的可复用性变差的缺点
- 它是动作正确性的保证,即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性
- 将强程序的健壮性,同时变更时可以做到非常良好的兼容性,提高程序的维护性可扩展性,降低需求变更时引入的新丰县
对里氏替换原则我的定义总结如下
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
- 子类中可以增加自己特有的方法
- 当子类的方法重载父类方法时,方法的前置条件(参数),要比父类的方法更宽松
- 当子类的方法实现父类方法时,方法的后置条件(返回值)要比父类的方法更严格或者相等
关于里氏替换原则的例子,最有名的是“正方形不是长方形”。当然,生活中也有很多类似的例子,例如,企鹅、鸵鸟和几维鸟从生物学的角度来划分,它们属于鸟类;但从类的继承关系来看,由于它们不能继承“鸟”会飞的功能,所以它们不能定义成“鸟”的子类。同样,由于“气球鱼”不会游泳,所以不能定义成“鱼”的子类;“玩具炮”炸不了敌人,所以不能定义成“炮”的子类等。
依赖倒置原则
高层模块不应该依赖底层模块,俩者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象
核心思想:要面向接口编程,不要面向实现编程
在软件设计中,细节具有多变性,而抽象层则相对稳定,因此以抽象层为基础搭建起来的架构要比以细节为基础搭建起来的架构要稳定得多.这里的抽象指的是接口或者抽象类,而细节是指具体实现类.
接口和抽象类的目的是制定好规范和契约,而不去设计任何具体操作,把展现细节的任务交给它们实现类去完成
主要作用如下
- 降低类间的耦合性
- 提高系统的稳定性
- 减少并行开发引起的风险
- 提高代码的可读性和维护性
遵循以下四点就能在项目中满足这个规则
- 每个类尽量提供接口或抽象类,或俩者都具备
- 变量的声明类型尽量是接口或者抽象类
- 任何类都不应该从具体类派生
- 使用集成时遵守里氏替换原则
例子:
客户去A商店购物,传入A商店
客户去A商店购物,传入B商店
正确方式A,B商店实现shop接口,用户的购物方法传入shop接口就好了
单一职责原则
一个类应该有且仅有一个引起变化的原因,否则类应该被拆分
这里的职责指类变化的原因.该原则提出对象不应该承担太多职责
如果一个对象承担了太多指责,存在以下俩个缺点
- 一个指责的变化可能会削弱或抑制这个类实现其他职责的能力
- 当客户端需要该对象的某一个指责时,不得不将其他不需要的职责都包含进来,从而造成冗余的代码
优点:
- 降低类的复杂度,一个类负责一个指责,比负责多项指责简单的多
- 提高类可读性
- 提高类的可维护性
- 降低变更引起的风险
例子
大学学生工作主要包括学生生活辅导和学生学业指导两个方面的工作,其中生活辅导主要包括班委建设、出勤统计、心理辅导、费用催缴、班级管理等工作,学业指导主要包括专业引导、学习辅导、科研指导、学习总结等工作。如果将这些工作交给一位老师负责显然不合理,正确的做法是生活辅导由辅导员负责,学业指导由学业导师负责
接口隔离原则
客户端不应该被迫依赖于它不使用的方法
根绝接口隔离原则,当一个接口太大时,我们需要将它分割成一些细小的接口,使用该接口的客户端只需知道与之相关的方法即可。每一个接口应该承担一种相对独立的角色,不干不该干的事情,干该干的事请。
含义:要为各个类建立它们需要的专用接口,而不要视图去建立一个很庞大的接口供所有的类去调用
接口隔离原则和单一指责都是为了提高类的内聚性,降低它们之间的耦合性,体现了封装的思想
- 单一指责原则注重的是指责,而接口隔离原则注重的是对接口依赖的隔离
- 单一指责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建
优点
- 将庞大的接口分解为多个粒度更小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性
- 接口隔离提高系统的内举行,减少了对外交互,降低了系统的耦合性
- 使用专门的接口还能体现对象的层次,可以通过接口的继承,实现对总接口的定义
- 减少项目工程中代码的冗余.过大接口里面通常防止许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码
实现接口隔离原则的时候,要根据以下几个规则来衡量
- 接口劲量小,但要有夏怒的,一个接口只服务于一个子模块或业务逻辑
- 为依赖接口的类定制服务.只提供调用者需要的方法,屏蔽不需要的方法
- 提高内聚,减少对外交互.使接口用最少的方法完成最多的事情
分析:学生成绩管理程序一般包含插入成绩、删除成绩、修改成绩、计算总分、计算均分、打印成绩信息、査询成绩信息等功能,如果将这些功能全部放到一个接口中显然不太合理,正确的做法是将它们分别放在输入模块、统计模块和打印模块等 3 个模块中,其类图如图 1 所示。
迪米特法则
如果俩个软件实体无需直接通信,那么就不应当发生直接的互相调用(不跟“陌生人”说话)
通过第三方转发该调用.其目的是降低类之间的耦合度,提高模块的相对独立性
迪米特法则中的朋友是指当前对象本身,当前对象的成员对象,当前对象锁创建的对象,当前对象的方法参数等,这些痛当前对象存在关联,聚合或者组合关系,可以直接访问这些对象的方法
优点
降低了类之间的耦合度,提高了模块的相对独立性
由于亲和度降低,从而提高了类的可复用性和系统的扩展性
但是迪米特法则会使系统中产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低.所以,在迪米特法则时需要反复权衡,确保高内聚的同事,保证系统的结构清晰
在运用迪米特法则时要注意以下六点
- 在类的划分上,一个创建弱耦合的类,类与类之间的耦合越弱,就越有利于实现可复用的目标
- 在类的设计上,劲量降低类成员的访问权限
- 在类的涉设计上,头衔考虑讲一个类设置成不变类
- 在对其他类的引用上,将引用他对象的次数将到最低
- 不暴露类的属性成员,而是一个提供相应的访问器(set,get方法)
- 谨慎使用serializable功能
例子:明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如与粉丝的见面会,与媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法
合成复用原则
软件在复用时,要尽量先试用组合或者聚合等关联关系来实现,其次才考虑集成关系来实现
如果要使用集成关系,啧必须严格遵守里氏替换原则,合成复用原则同里氏替换原则相辅相成的,俩者都开闭原则的具体实现规范
- 通常类的复用破坏了类的封装性.因为集成会将父类的实现细节暴露给子类,父类对子类是透明的,这种复用又称为白箱复用
- 子类与父类的耦合度搞.父类的实现的任何改变都会导致子类的实现发生变化,这不利于扩展与维护
- 他限制了复用的灵活性.从弗雷继承而来的实现是静态的,在编译时就已经定义,所以在运行时不可能发生改变
采用组合或聚合复用时,可以将已有的对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点
- 它维持了类的封装性.因为成分对象的内部细节是对新对象看不见的,所以这种复用又称为黑箱复用
- 新旧类之间的耦合度低.这种复用所需的依赖较少,新对象存取成分对象的唯一方式是通过成分对象的接口
- 复用的灵活性搞.这种复用可以在运行时动态进行,新对象可以动态地引用成分对象类型相同的对象
合成复用原则是通过将已有的对象纳入新的对象中,作为新对象的成员来实现的,新对象可以调用 已有对象的功能,从而达到复用
例子:汽车按“动力源”划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。如果改用组合关系实现就能很好地解决以上问题,
总结
设计原则 | 概括 | 目的 |
---|---|---|
开闭原则 | 对扩展开放,对修改关闭 | 降低维护带来的风险 |
依赖倒置原则 | 高层模块和低层模块都依赖于抽象,要面向接口编程 | 更利于代码结构的升级扩展 |
单一指责原则 | 一个类只干一件事情,实现类要单一 | 便于理解,提高代码可读性 |
接口隔离原则 | 一个接口只干一件事情,接口要精简单一 | 功能解耦,高聚合,低耦合 |
迪米特法则 | 一个类应该保持对其他的对象最少的了解,降低耦合度 | 之和朋友交流,不和陌生人说话 |
里氏替换原则 | 不要破坏集成关系,子类重写方法功能发生改变,不应该影响父类方法的含义 | 防止继承泛滥 |
合成复用原则 | 劲量使用组合或者聚合关系实现代码复用,少使用继承 | 降低代码耦合 |