23种设计模式
2022-04-08 16:15 张紫荣 阅读(195) 评论(0) 编辑 收藏 举报分类
设计模式分为三大类:
创建型模式:工厂方法、抽象工厂、单例、建造者、原型。对象实例化的模式,创建型模式用于解耦对象的实例化过程。
结构型模式:适配器、装饰器、代理、外观、桥接、组合、享元。把类或对象结合在一起形成一个更大的结构。
行为型模式:策略、模板方法、观察者、迭代子模式、责任链、命令、备忘录、状态、访问者、中介者、解释器。类和对象的交互,及责任的算法。
参考地址:
https://blog.csdn.net/A1342772/article/details/91349142
https://www.runoob.com/design-pattern/design-pattern-tutorial.html
创建者模式:
1、工厂方法模式
A、简单工厂模式(Simple Factory Pattern):又称静态工厂方法(Static Factory Method)模式。根据参数的不同返回不同类的实例我。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同父类。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161526695-1651419427.png)
工厂类承担对象有构建职责(对外无需暴露创建逻辑),新添加产品时,需要在Factory类中添加新的分支,与开闭原则(Open Close Principle,OCP)相违背。
B、工厂方法模式(Factory Method Pattern)
工厂方法模式将产品类的实例化操作延迟到工厂子类中完成。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161525644-732842091.png)
工厂父类负责定义创新对象的公共接口,而工厂子类则负责生成具体的产品对象,通过工厂子类来确定究竟应该实例化哪一个具体产品类。
* 增加新的产品时无须修改现有系统,并封装了产品对象的创建细节;
* 增加新产品时需要增加新的工厂,导致系统类的个数成对增加,一定程度上增加了系统的复杂性。
* 客户端需要关联工厂类的具体实例。
2、 抽象工厂模式
抽象工厂模式(abstract factory pattern),提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。
- 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
- 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161526958-1021294730.png)
很好的体现了“开闭原则”,隔离具体类的生成。
每次可以通过具体工厂类创建一个产品族中的多个对象,协商或者替换产品族比较方便
增加新的产品等级结构复杂,需要个性抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。
3、单例模式:
定义:确保一个类最多只有一个实例,并提供一个全局访问点。
预加载:线程安全,在类加载时就初始化。
public class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }
懒加载:线程安全的方式。
public class Singleton{ private static volatile Singleton instance = null; private Singleton(){} //方法中的synchronized应该可以去掉,只保留方法内部的同步限制 public static synchronized Singleton getInstance(){ if (instance == null){ synchronized(instance){ if (instance == null){ instance = new Singleton(); } } } return instance; } }
如上代码中,private static volatile Singleton instance, instance 需要使用volatile来修饰。是因为在语句"instance=new Singleton()" 原子操作,因为对象创建时分三部操作:
- memory = allocate(); // 1 初始化内存空间
- ctorInstance(memory); //2 初始化对象
- instance = memort(); //3 设置instance 指向赂分配的内存地址
JVM为了提高程序执行性能,会对没有依赖的代码进行重排序,上面的2和3行代码可能被重新排序。也就是有线程一执行了1、3,还未执行2的时候,另一线程可能判断instance为null,进而继续创新单例类。
4、建造者模式:
建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示相分离,使得同样的构建过程可以创建不同的表示。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161525470-854169463.png)
建造者模式需要在Director类中构构Product中的每一个部分,而工厂模式通常调用一个工厂方法就可以生成一个对象。
5、原型模式:
原型模式(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161525931-1632102066.png)
Java中的Cloneable就是原型模式的例子。包括浅拷贝和深拷贝。
原型模式的本质就是clone,可以解决构建复杂对象的资源消耗问题,能再某些场景中提升构建对象的效率;还有一个重要的用途就是保护性拷贝,可以通过返回一个拷贝对象的形式,实现只读的限制。
结构型模式:
1、适配器模式
适配器(Adapter),将某个类的接口转换成客户期望的另一个接口表示,目的是消除由于接口不匹配所造成的兼容性问题。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161526788-284127224.png)
优点:将目标类和适配者类解耦,通过引入一下适配器类来重用现有的适配者类,而无须修改原有代码;增加了类的透明性和复用性。
在设计的时候尽量不添加适配器类。
2、装饰器模式:
装饰器(Decorator) 允许向一个现有的对象添加新的功能,同时又不改变其结构。其别名为包装器(Wrapper)
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161526974-642407508.png)
与继承相比,关联关系的主要优势是不破坏类的封装性,而且继承是一种耦合度较大的静态关系,无法在程序运行时动态扩展。
若要扩展功能,装饰模式提供了比继承更加弹性的实现方式。
- 装饰类与Component类继承同一对象,对客户端来说无论是装饰前的对象还是装饰后的对象来说,都可以相同对待。
- 尽量不要把太多的逻辑放到具体Component类中。
3、代理模式
代理(Proxy Pattern):给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161526956-1171315128.png)
优点:
* 代理模式能够协调调用者和被调用者,在一定程序上降低了系统的耦合度。
* 远程代理使得客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户请求。
* 虚拟代理通过使用一个小对象来代表一个大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。
* 保护代理可以控制对真实对象的使用权限。
静态代理:通过编写具体的类实现。
JDK动态代理:与静态代理相似,都要创建代理类,代理类都要实现接口,但动态代理不知道要针对哪个接口、哪个被代理类创建代理类,因为它是运行时被创建的。
CGLIB代理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法,在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。底层使用字节码处理框架ASM,来转换字节码并生成新的类。
4、外观模式
外观模式(Facade):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口中提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161526964-1883032123.png)
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161525939-691838088.png)
外观模式提供了一个统一的接口,用来访问子系统中的一群接口,它让一个应用程序中子系统间的相互依赖关系减少到了最少。客户端不需要知道子系统的内部实现,只需要跟Facade交互即可。
5 桥接模式:
桥接(Bridge)模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161525961-191369528.png)
桥接模式的主要优点是分离抽象接口及其实现部分,是比多继承方案更好的解决方法,桥接模式还提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。(如果一个对象有两种或者多种分类方式,并且两种分类方式都容易变化)
6 享元模式
享元(Flyweight)模式:通过共享的方式高效的支持大量细粒度的对象。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161526542-1971443352.png)
享元模式的核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。
7 组合模式
组合模式组合多个对象形成树形结构以表示“整体-部分”的结构层次。它定义了如何将容器对象和叶子对象进行递归组合,使得客户在使用的过程中无须进行区分,可以对他们进行一致的处理。在使用组合模式中需要注意一点也是组合模式最关键的地方:叶子对象和组合对象实现相同的接口。这就是组合模式能够将叶子节点和对象节点进行一致处理的原因。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161526318-1730385563.png)
行为型模式:
1 状态模式:
状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
何时使用:代码中包含了大量与对象状态有关的条件语句,可用于消除if...else等条件语句
状态模式的接口中有一个或者多个方法。将所有与某个状态有关的行为放到一个类中,并可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161526728-1132744904.png)
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161526728-1132744904.png)
状态模式引入了一个抽象类来专门表示对象的状态,并且可以更换状态
* 会增加系统类和对象的个数
* 状态模式与策略模式的差别,状态模式的变化是内部的转变,不是通过外部调用修改的。
2 策略模式:
策略模式(Strategy):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161526295-392311991.png)
* 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
* 缺点: 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
3 命令模式:
命令模式(Command):将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161526324-432510057.png)
Receiver真正的命令执行对象。
Command: 声明执行命令的接口,拥有执行命令的抽象方法execute()
Concrete Command:抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
Invoker:请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
4 观察者模式:
观察者(Observer)模式:定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161526916-305766439.png)
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161527148-475372152.png)
- 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
- 观察者模式在观察目标和观察者之间建立一个抽象的耦合。
- 观察者模式支持广播通信。
- 观察者模式符合“开闭原则”的要求
* Java中已有对观察者的支持类
* 顺序执行,若某一观察者错误会导致系统卡死,可以采用异步方式。
5 模板方法
模板(Template)模式:定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。
优点: 封装不变的部分,扩展可变的部分;提取公共代码,便于维护;行为由父类控制,子类实现。
6 责任链
责任链(Chain of Responsibility Pattern),为请求创建了一个接收者对象的链。避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请示,直到有对象处理它为止。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161525614-1640397254.png)
关键代码:Handler里面聚合它自己,在HandlerRequest里判断是否合适,如果没有达到条件则向下传递,向谁传递之前Set进行。
7 迭代器模式
迭代(Iterator)模式:提供一种方法顺序访问一个聚合对象中各个元素,而又无需暴露该对象的内部表示。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161527021-1252043316.png)
* 访问一个聚合对象的内容而无须暴露它的内部表示
* 需要为聚合对象提供多种遍历方式
* 为遍历不同的聚合结构提供一个统一的接口
8 备忘录
备忘录(Memento Pattern) :保存一个对象的某个状态,以使在适当的时间恢复对象,又叫快照模式。
* 提供了一种可恢复状态的机制,当用户需要时能够比较方便地将数据恢复到某个历史的状态。
* 实现了内部状态的封装,除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
* 简化了发起人,发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
9 访问者
访问者(Visitor)模式:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不nthy变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161527070-366303305.png)
* 抽象访问者(Visitor)定义了一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit(),该操作中的参数类型标识了被访问的具体元素。
* 具体访问者(ConcreteVisitor)实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么
* 抽象元素(Element):声明一个包含接受操作accept()的接口,被接受的访问者对象作为accept()方法的参数。
* 具体元素(ConcreteElement):实现抽象元素角色提供的accept()操作,其方法体通常都是visitor.visit(this),另个具体元素中可能还包含本身业务逻辑的相关操作。
* 对象结构(Object Structure):是一个包含元素角色的窗口,提供让访问者对象遍历容器中的所有元素的方法,通常由List、Set、Map等聚合类实现。
10 中介者
中介者(Mediator)是用来降低多个对象和类之间的通信复杂性,这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161525959-1743272043.png)
抽象中介者(Mediator):它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
具体中介者(ConcreteMediator):实现中介者接口,定义一个List来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
抽象同事类(Colleague):定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能
具体同事类(ConcreteColleague):抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。
11 解释器
解释器(Interpreter)给定一个语言,定义它的文法的一种表示,并定义一个解释器。这个解释器使用该表示来解释语言中的句子。
![](https://img2022.cnblogs.com/blog/599808/202204/599808-20220408161525943-1880891996.png)
抽象表达式角色(Abstract Expression):该角色声明一个所有的具体表达式角色都需要实现的抽象接口,该接口中主要是一个解释操作interpert方法
终结符号表达式(Terminal Expression):实现了抽象表达式角色所要求的接口,方法中的每一个终结符都有一个具体终结表达式与之对应。
非终结符号表达式(Nonterminal Expression):一个具角色,文法中的每一条规则都对应一个非终结符表达式类。
环境(Context):提供解释器之外的一些全局信息。
客户端(Client):创建一个抽象语法以数据,调用解释操作。