设计模式
2020-04-07 20:34:09 47 举报
AI智能生成
面对对象的设计模式
作者其他创作
大纲/内容
行为型模式
模板方法
定义一个操作中的算法框架,而将一些步骤延迟到之类中,使之类在不改变一个算法的结构,即可重新定义该算法的特定步骤
优点
封装不变部分,扩展可变部分
提取公共部分代码,便于维护
行为由父类控制,子类实现
缺点
之类对父类产生了影响(操作步骤延迟到子类实现)
带来代码阅读的难度,不利于上手
策略
定义一组算法,将每个算法都封装起来,并且使他们都可以互换
优点
算法可以之间自由切换
避免使用多重判断条件
扩展性能好
缺点
策略类数量增多
所有策略类都需要向外暴露(上层模块必须知道有哪些策略,才能使用哪些模块。违反了迪米特法则)
命令
将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求的日志,可以提供命令的撤销和恢复的功能
优点
类间解耦
可扩展性
命令模式结合其他模式效果更好(结合责任链模式,实现命令族的解析任务,结合模板方法,减少命令之类的数量膨胀的问题)
缺点
命令子类会较多,使得类膨胀很大
责任链
使多个对象有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系,将这些对象连成链,并沿着链传递请求,直到有对象处理为止
优点
将请求和处理分开,两者解耦,提高系统的灵活性(请求中不关心谁处理了我,处理者也不必知道请求的全貌)
缺点
性能问题:请求都是从链头到链尾,特别是链比较长的时候
调试代码不方便,链条比较长,调试逻辑复杂
状态
状态变更引起行为变更,看起来就像类改变了一样
优点
结构清晰
遵循设计原则(开闭和单一职责)
封装性好
缺点
类膨胀的问题
应用场景
行为随状态变更而变更
条件,分支判断语句的替代者
观察
也称为发布订阅模式,对象间一种一对多的依赖关系,使得每当对象改变状态时,则所有依赖对象都会得到通知并更新
优点
观察者和被观察者之间时抽象耦合的
可以建立触发通知体系
缺点
开发效率和运行效率,开发和调试比较复杂,而且消息一般是顺序通知的,有可能会导致整体运行效率问题(单点问题)
需注意问题
广播链的问题(消息级联太多)
异步处理的问题
中介者
用一个中介对象封装一系列的对象交互操作,中介者使对象不需要显示的相互作用,从而使其耦合松散,而且可以独立的更改他们之间的交互
优点
减少类之间的依赖,把原有一对多依赖改成一对一的依赖,都依赖中介者,也降低类之间的耦合性
缺点
中介者会膨胀的很大,而且逻辑复杂,原本N个对象直接依赖相互依赖关系转换为中介者和同事类间的依赖关系,同事类越多,中介者的逻辑越复杂
使用场景
中介者模式适用于多个对象之间紧密耦合的情况,在类图中,出现了网状结构,通过中介者梳理为星型结构
迭代器
提供一种方法访问容器中各个元素,而又不需要暴露类的内部细节
java已经提供了Iterable接口,在开发的时候,无需在定义接口及其操作
备忘录
在不破坏封装性的前提下,捕获一个对象内部状态,并在该对象在外保存这个状态,这样就可以将对象恢复到原有状态
使用场景
保存和恢复数据额状态场景
注意事项
备忘录的生命周期(不需要就应该删除)
备忘录的性能(不要频繁建立备忘录)
解释器
给定一门语言,定义文法的表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子
优点
易于扩展
缺点
类膨胀的问题
采用递归调用
效率问题(该模式使用大量的循环和递归)
访问者
封装一些作用与某种数据结构的元素操作,它可以在不改变数据结构的前提下定义作用于这些元素新的操作
优点
符合单一职责原则
扩展性能好
灵活性高
缺点
具体元素对访问者公布细节
具体元素变更比较困难
违背依赖倒置原则(访问者依赖了具体元素,而不是抽象元素)
使用场景
对象结构包含多类对象,它们拥有不同的接口,而想实施些依赖于其具体类的操作,迭代器模式不能胜任的场景
对一个对象结构进行很多不同且不相关的操作,而想避免这些操作污染对象
结构性模式
代理
为其他对象提供一种代理,来控制对象的访问
优点
职责清晰,真实角色实现相关业务逻辑不用关心其他事务
高扩展性,具体主题角色随时可以发生变化,只要实现了接口,代理类在不做修改的情况下,可以继续使用
spring AOP的实现
适配器
将一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配的类可以协调工作
优点
使两类能够协调配合工作
增加类的透明性(对接口的访问都委托给适配器类了)
灵活性好,不需要适配器时,直接删除即可
桥接
将抽象和实现解耦,使两者可以独立地变化
优点
抽象和实现的分离
优秀的扩展的能力
实现细节对客户透明
场景
不希望或者不适合使用继承的场景
接口或者抽象类不稳定的场景
重用性要求较高的场景
装饰
动态地给一个对象添加额外的功能,比生成子类更灵活
优点
装饰类和被装饰类可以独立发展,而不会互相耦合
继承(覆盖父类方法)关系的替代方案
可以动态的扩展实现一个类的功能
缺点
多层装饰增加系统的复杂度,不便于调试和维护
外观
也称为门面模式,要求一个系统的外部通信与其内部通信必须通过一个统一的对象进行,提供一个高层次接口,使子系统更易于使用
优点
减少系统间的相互依赖
提高系统的灵活性
提高系统的安全性(提供统一访问接口)
缺点
不符合开闭原则,一旦门面对象出问题,不得不修改门面角色
使用场景
为复杂模块或者对象提供供外界访问的接口
子系统相对独立,外界对系统访问只要是黑箱操作即可
预防低水平人员带来的风险
nginx
享元
使用共享对象可有效的支持大量细粒程度的对象
优点
减少创建的对象(减少对象的创建时间),降低内存开销
缺点
需要分离外部状态和内部状态,提高系统的复杂性
使用场景
系统中存在大量相似对象
需要缓冲池
组合(合成,部分-整体)
将对象组合成树形结构以表示“部分-整体”的层次关系,使得用户对单个对象和组合对象的使用具有一致性
优点
高层模块调用简单
节点自由增加
缺点
直接使用了实现类,与依赖倒置原则冲突
使用场景
维护和展示部分场景之间的关系,如树形菜单,文件和文件夹管理
从整体能够独立处部分模块或功能的场景
设计模式7大原则
1.单一职责
定义:规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分
优点:
降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多。
提高类的可读性。复杂性降低,自然其可读性会提高。
提高系统的可维护性。可读性提高,那自然更容易维护了。
变更引起的风险降低。变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可以显著降低对其他功能的影响。
2.开闭原则
1.软件实体应当对扩展开放,对修改关闭
优点
软件遵守开闭原则的话,软件测试时只需要对扩展的代码进行测试就可以了,因为原有的测试代码仍然能够正常运行。
粒度越小,被复用的可能性就越大;在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性。
遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护。
3.里氏替换原则
继承必须确保超类所拥有的性质在子类中仍然成立
优点
里氏替换原则是实现开闭原则的重要方式之一
它克服了继承中重写父类造成的可复用性变差的缺点
它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。
4.依赖倒置原则
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象
其核心思想是:要面向接口编程,不要面向实现编程。
其核心思想是:要面向接口编程,不要面向实现编程。
优点
降低类间的耦合性
提高系统的稳定性
减少并行开发引起的风险
提高代码的可读性和可维护性
5.接口隔离原则
一个类对另一个类的依赖应该建立在最小的接口上。要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用
优点
将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性
接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性
如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险
使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义
能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码
与单一职责的区别
单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。
单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。
6.迪米特法则
又叫作最少知识原则:一个对象对其他对象有最少的了解,通俗的讲,一个类应该对自己需要耦合和调用的类知道最少。
优点
降低了类之间的耦合度,提高了模块的相对独立性
由于亲合度降低,从而提高了类的可复用率和系统的扩展性
7.合成复用原则
又叫组合/聚合复用原则。它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
继承复用的缺点
承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。
合成复用的优点
它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。
新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。
创建型模式
单例
确保每一个类只有一个实例
优点
减少内存开支
避免资源多重占用
减少系统性能开销(尤其是当对象产生消耗较多的资源)
缺点
扩展困难
单例模式对测试不利
与单一职责原则相冲突
原型
用原型实例指定创建对象的种类,并且通过拷贝这些原型对象创建新的对象(实现Cloneable方法)
优点
性能好,创建对象快
避免了构造函数的约束
使用场景
资源初始化场景
性能和安全的要求(不需要调用构造方法)
同一对象多个修改者
注意事项
浅拷贝和深拷贝的问题
构造方法不执行
工厂方法
定义一个用于创建对象的接口,让子类决定实例化哪一个类
优点
良好的封装性
扩展性优秀
屏蔽产品类的变动,产品类变动,只要接口不变不用生产产品的代码
典型的解耦架构
抽象工厂
为创建一组相关或相互依赖的对象提供一个接口,而且无需执行他们的具体类
优点
封装性,每个产品的实现类不是高层模块所关心的,主要关心的是创建对象的接口
产品族内部的约束为非公开的状态
缺点
产品族扩展困难,需要修改的部分比较多
建造者
也叫做生成器模式,将一个复杂对象的构建和他的表示相分离,使得同样的构建过程可以创造不同的表示
优点
封装性:使客户端不知产品的细节
建造者互相独立,容易扩展
便于控制细节风险
与工厂方法的区别
其主要功能是基本方法调用顺序的安排,也就是说这些基本方法已经实现了,通俗的讲就是零件的装配,顺序不同,产生的对象不同
工厂方法的主要关注点是创建对象,创建对象的细节,其不关心,零件的装配顺序也不关心
收藏
收藏
0 条评论
下一页