软件设计模式 概念: 是一套反复使用的、多数人知晓的、经过分类编目的代码设计经验总结。是一种用于对软件系统中不断重现的设计问题的解决方案进行文档化的技术,是一种共享专家设计经验的技术。
目的: 在特定的环境下为了解决某一通用软件设计问题提供的一套定制解决方案,描述了对象和类之间的相互作用。
基本要素: 模式名称、问题、目的、解决方案、效果、实例代码、相关设计模式。
分类: 根据目的分类: 创建型设计模式:用于对象的创建。
结构型设计模式:用于处理对象或类之间的组合。
行为型设计模式:描述类或对象怎样交互怎样分配职责的。
根据范围分类: 类模式:处理类和子类之间的关系,通过继承建立,是一种静态的关系。
对象模式:处理对象之间的关系,更具动态性。
优点: 融合了众多专家的经验,可以更加简单方便地复用成功的设计和体系结构,设计方案更加灵活且易于修改,提高软件系统的开发效率和软件质量,有助于初学者更深入的学习面向对象思想。
设计原则: 单一职责原则: 一个对象只应当有一个职责,并且应该被完整的封装在一个类中。
开闭原则: 对象应该对修改关闭,对扩展开放。
依赖倒转原则: 高层模块不应该依赖于低层模块,它们都应该依赖于抽象;抽象不应当依赖于细节,细节应当依赖于抽象。
接口隔离原则: 客户端不应该依赖它不需要的接口。
里氏代换原则: 在引用基类的地方都应当可以透明的引用起子类的对象。
合成复用原则: 在复用一个模块的时候,应当尽量避免使用继承,优先使用对象组合的方式。
迪米特法则: 每一个软件单位都应该对其他软件单位有最少的认知,而且仅局限于与本单位密切相关的软件单位。
一、创建型模式 概念: 关注对象的创建过程,对类的实例化的过程进行了抽象,将软件模块中对象的创建和使用分离,对用户隐藏了创建的细节。
优点: 降低了系统的耦合程度,让设计方案更易于修改和拓展,让用户在使用对象时无需关心对象创建的细节。
(一)简单工厂模式 概念: 定义一个工厂类,根据传入的参数不同返回不同类的实例,被创建的类都具有相同的父类。
角色: 工厂类、抽象产品角色、具体产品角色
优点: 实现了对象的创建和使用的分离,无需知道对应的类名只需要知道具体产品类所对应的参数,提高了系统的灵活性。
缺点: 工厂类的职责过重,会增加系统中类的个数,增加了系统的复杂度和理解难度,系统扩展困难,工厂角色无法形成基于继承的等级结构。
适用环境: 工厂类负责创建的比较少时,客户端只知道传入工厂类的参数,对如何创建对象并不关心。
(二)工厂方法模式 概念: 定义一个创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
角色: 抽象工厂类、具体工厂类、抽象产品类、具体产品类
优点: 工厂方法创建了客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节。
能够让工厂自主决定创建何种产品对象,而如何创建这个对象的细节将完全封装在具体工厂内部。
很好的符合了开闭原则。
缺点: 系统中类的个数将成对的增加,引入抽象层增加了系统的抽象性和理解难度。
适用环境: 客户端不知道它所需要的对象的类。抽象工厂类通过其子类来指定创建哪个对象。
(三)原型模式 概念: 使用原型实例来指定待创建对象的类型,并且通过复制这个原型来创建新的对象。
角色: 抽象原型类、具体原型类、客户类
优点: 简化对象的创建过程,提高创建新实例的效率。
拓展性较好。
提供了简化的创建结构。
可以用深克隆的方式保存对象的状态,可以辅助实现撤销操作。
缺点: 要为每个类配备一个克隆方法在内部,要改造时就要修改源代码,违反了开闭原则。
在实现深克隆时要编写较为复杂的代码,实现起来可能会过于麻烦。
适用环境: 创建新对象的成本较大时。
需要保存对象的状态同时对象的变化很小时。
要避免使用分层的工厂类来创建分层次的对象时。
(四)单例模式 概念: 确保一个类只有一个实例,同时提供一个全局的访问点来访问这个唯一的实例。
角色: 单例类
拓展: 饿汉式单例: 在定义静态变量时实例化单例类,在类加载时对象就已经被创建。
资源利用率低,内存占用多,系统加载事件长。
懒汉式单例: 在第一次被引用时将自己实例化,被加载时不会将自己实例化。
在需要时才将自己实例化。
优点: 提供了对唯一实例的受控访问。节约了系统资源,提高了系统性能。允许可变数目的实例。
缺点: 单例类的职责过重,违反了单一职责原则。
单例类没有抽象层,扩展有很大的困难。
实例化的对象如果长期不用会被系统认为是垃圾,会自动回收并销毁。
适用环境: 系统只需要一个实例对象。
系统的单个实例只允许使用一个公共访问点。
二、结构型模式 概念: 将现有的类或对象组织在一起形成更加强大的结构。可以分为类结构型和对象结构型。
优点: 通过关联关系在一个类中定义另一个类的实例对象。比较好的符合了合成复用原则。
(一)适配器模式 概念: 将一个类的接口转换成客户希望的另一个接口,适配器让那些接口不兼容的类可以一起工作。
角色: 适配者、适配器、目标抽象类
拓展: 类适配器:Adapter不能适配Adaptee的子类。
对象适配器:Adaptee中是否为final不影响最终的结果。
优点: 将目标类和适配者类解耦。
增加了类的透明性和复用性。
灵活性和拓展性都非常好。
对象适配器优点: 可以把多个适配者适配到同一个目标。
可以适配一个适配者的子类。
缺点: 对于不支持多重类继承的语言,一次只能适配一个适配者。
适配者类不能为最终类。
目标抽象类只能为接口类,有一定的局限性。
对象适配器缺点: 在适配器中置换适配者类的某些方法比较麻烦。
适用环境: 系统需要使用一些现有的类。
想要创建一个可以重复工作的类。
(二)桥接模式 概念: 将抽象部分与他的实现部分解耦,使得两者都可以独立的变化。
角色: 抽象类、扩充抽象类、实现接口类、具体实现类
优点: 分离了抽象接口及其实现部分。
可以取代多层继承的方案,减少子类的个数。
提高系统的可拓展性,符合开闭原则。
缺点: 增加系统的理解与设计难度。
正确识别两个独立的维度需要一定的经验积累。
适用环境: 系统要在抽象化和具体化之间增加更多的灵活性。
一个类存在两个或多个变化的维度。
抽象的部分和实现的部分可以用继承的方式独立拓展而互不影响。
不希望因继承导致系统中的类急剧增加的系统。
(三)装饰模式 概念: 动态的给一个类增加一些额外的职责,装饰模式提供了一个比使用子类更加灵活的替代方案。
角色: 抽象构件、具体构件、抽象装饰类、具体装饰类
优点: 拓展一个功能比继承更加灵活。
可以通过一种动态的方式扩展一个对象的功能。
可以对一个对象进行多次装饰。
具体构件类和具体装饰类可以独立的变化。
缺点: 在使用装饰模式时会产生许多小对象,大量小对象会消耗系统资源,在一定程度上影响程序的性能。
装饰模式比继承模式更加容易出错,排错也更加困难。
要逐级排查错误,较为繁琐。
适用环境: 不影响其他对象的情况下动态透明的给单个对象添加职责。
不能采用继承的方式拓展系统时,或者采用继承方式会对系统造成不利时。
(四)代理模式 概念: 给一个对象提供一个代理或者一个占位符,并由代理对象来控制对原对象的访问。
角色: 代理类、真实主题角色、抽象主题角色
优点: 能够协调调用者和被调用者,降低了系统的耦合程度。
可以针对抽象主题角色进行编程,符合开闭原则,系统具有较好的灵活性和可拓展性。
其他代理模式的优点: 远程代理提高了系统的整体运行效率。
虚拟代理一定程度上节省了系统的开销。
缓冲代理提供了临时缓存存储空间,优化了系统性能,缩短了执行时间。
保护代理可以控制对象的访问权限,为不同用户提供不同等级的权限。
缺点: 在客户端和正式主题之间增加了代理,系统请求处理的速度变慢。
实现代理需要额外的工作,而且有些代理的实现较为复杂。
适用环境: 客户端需要访问远程主机时。
需要消耗较少资源的对象代表一个消耗资源较大的对象。
为频繁访问的操作结果提供一个临时的存储空间时。
要控制一个对象的访问为不同级别的用户提供不同的权限时。
需要为一个对象的访问提供一些额外的操作时。
三、行为型模式 概念: 关注系统中对象的交互,系统运行时对象之间的相互通信与协作,还关注它们之间的相互作用和职责划分。
优点: 满足合成复用原则的要求
(一)命令模式 概念: 将一个请求封装成一个对象,从而可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。
角色: 抽象命令类、具体命令类、调用者、接收者
优点: 降低了系统的耦合度。
新的命令可以很容易的加入到系统中。
可以比较容易的设计出一个命令队或宏命令。
为请求的撤销和恢复操作提供了一种设计方案。
缺点: 可能会导致某些系统会拥有过多的具体命令,会影响命令模式的使用。
适用环境: 系统要将请求调用者和请求接受者解耦。
系统要在不同的时间指定请求、将请求排队、执行请求。
系统需要支持命令的撤销和恢复操作。
系统需要将一组操作组合在一起形成宏命令。
(二)观察者模式 概念: 定义了对象之间的一种一对多的依赖关系,使得每当一个对象的状态发生变化时,其相关依赖对象皆得到通知,并自动更新。
角色: 观察者、具体观察者、目标、具体目标
优点: 可以实现表示层和数据逻辑层的分离。
在观察目标和观察者之间建立了一个抽象的耦合。
支持广播通信。
符合开闭原则。
缺点: 如果目标的观察者较多,给所有观察者发送通知会花费很多时间。
如果观察者和观察目标之间存在循环依赖,观察目标会触发它们之间的循环调用,会导致系统崩溃。
观察者不知道目标是怎么变化的,只知道目标变化了。
适用环境: 一个模型有两个方面,其中一个方面依赖于另一个方面,将两个方面封装到独立的对象中可以使得它们可以独自的改变和复用。
一个类改变导致一个或多个其他的对象也发生改变,不知道具体有多少类将发生改变,也不知道这些对象是谁。
需要在系统中创建一个触发链。
评论