经典设计模式
二十三种经典设计模式
软件设计模式可以让成功的设计和体系结构更加简单方便的复用。它通常包含以下几个基本要素:模式名称、别名、动机、问题、解决方案、、效果、模式角色、合作关系、实现方法、实用性、已知应用、例程、模式扩展和相关模式等。
设计模式六大原则
- 单一职责原则(SRP)
单一职责原则是指让一个类只负责一件事,当这个类需要负责多件事时,就应该考虑将其拆分成多个类。单一职责原则可以看成是低耦合、高内聚在 oop 上的引申。 - 开放封闭原则(OCP)
开放封闭原则是指类对外扩展开放,对内修改关闭。其核心思想是抽象编程,而不是具象编程。 - 里氏替换原则(LSP)
里氏替换原则是指父类可以出现的地方,子类一定可以出现。只有子类能够替换父类时,才能保证程序在运行期间识别子类,这是保证继承复用的基础。 - 接口隔离原则(ISP)
接口隔离原则是指使用多个功能单一的接口替换一个功能复杂的接口。分离的方式有两种:- 一是委托分离,即增加一个新的类型来委托客户的请求,隔离客户和接口的直接依赖,但是会增加系统开销。
- 二是多重继承分离,即通过接口多重继承来实现客户需求。
- 依赖倒置原则(DIP)
依赖倒置原则是依赖于具象,即高层模块不应该依赖于底层模块,抽象不依赖于具体,具体依赖于抽象。 - 迪米特法则(最少知道原则)(LKP)
迪米特法则又称最少知道原则,是指一个类应尽可能少的了解其它类。
设计模式分类
设计模式可根据模式的目的和模式的作用对象两种方式来分类。
- 根据模式目的分类
- 创建型模式
用于描述 “怎么创建对象”。它的主要特点是将对象的创建和使用分离。如:单例、原型、工厂方法、抽象工厂、建造者等五种创建型模式。 - 结构型模式
用于描述 “如何将类或对象按照某种布局组成更大的结构”。如:代理、适配、桥接、装饰、外观、享元、组合等七种结构型模式。 - 行为型模式
用于描述 “类或对象之间怎样相互协作来完成单个对象无法单独完成的任务,以及怎样分配职责”。如:模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等十一种行为型模式。
- 创建型模式
- 根据模式作用对象分类
- 类模式(即作用于类)
用来处理父类与子类之间的关系,这些关系是通过继承建立的,是静态的,在编译时边确定下来了。如:工厂、适配、模板方法、解释器等四种类模式。 - 对象模式(即作用于对象)
用于处理对象之间的关系,这些关系可以通过组合和聚合来实现,在运行时刻是可以变化的,更具动态性。
- 类模式(即作用于类)
作用对象 / 目的 | 创建型模式 | 结构型模式 | 行为型模式 |
---|---|---|---|
类模式 | 工厂方法 | (类)适配器 | 模板方法 解释器 |
对象模式 | 单例 原型 抽象工厂 建造者 | 代理 (对象)适配器 桥接 装饰 外观 享元 组合 | 策略 命令 职责链 状态 观察者 中介者 迭代器 访问者 备忘录 |
二十三种设计模式之间的关系:
(注:图片取自 23种设计模式全解析 - codeTao - 博客园 (cnblogs.com))
二十三种设计模式简介
- 1、单例(Singleton)模式
某个类只能生成一个实例,且该类提供了一个全局访问点,供外部获取该实例。其拓展是有限多个实例。 - 2、原型(Prototype)模式
将一个对象作为原型,通过对其复制而克隆出多个与原型类似的新实例。 - 3、工厂方法(Factory Method)模式
定义一个用户创建产品的接口,由子类决定生产什么产品。 - 4、抽象工厂(Abstract Method)模式
提供一个创建产品族的接口,其每个子类可以生成一系列相关的产品。 - 5、建造者(Builder)模式
将一个复杂的对象分解成多个简单的部分,然后根据不同需要分别创建它们,最后把它们构建成复杂的对象。 - 6、代理(Proxy)模式
为某个对象提供一种代理以控制对对象的访问。即客户端通过代理间接的访问该对象,从而限制、增强、修改该对象的一些特征。 - 7、适配器(Adapter)模式
将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能在一起工作的那些类能够在一起工作。 - 8、桥接(Bridge)模式
将抽象与实现分离,使它们可以独立变化。它是用组合来代替继承实现,从而降低 “抽象和实现” 这两个可变维度的耦合度。 - 9、装饰(Decorator)模式
动态的给对象增加一些职责,即增加其额外功能。 - 10、外观(Facade)模式
为多个复杂的子系统提供一个统一的接口,使得这些子系统更加容易被访问。 - 11、享元(Flyweight)模式
运用共享技术来有效的支持大量细粒度的对象的复用。 - 12、组合(Composite)模式
将对象组合成树状的层次结构,使用户对单个对象和组合对象有一致的访问性。 - 13、模板方法(Template Method)模式
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以在不改变该算法结构的情况下重新定义该算法的某些特点步骤。 - 14、策略(Strategy)模式
定义了一些列算法,并将每个算法封装起来,使得它们可以相互替换,且算法的改变不会影响算法用户的使用。 - 15、命令(Command)模式
将一个请求封装为一个对象,使得发出请求的责任和执行请求的责任分开。 - 16、职责链(Chain of responsibility)模式
把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这个方式去除对象之间的耦合。 - 17、状态(State)模式
允许一个对象在其内部状态发生改变时改变其行为能力。 - 18、观察者(Observer)模式
多个对象之间存在一对多的关系,当一个对象发生改变时,把这种改变通知给其它对象,从而影响其它对象的行为能力。 - 19、中介者(Mediator)模式
定义一个中介对象来简化多个原有对象之间的关系,降低系统的耦合度,使得原有对象之间不必相互了解。 - 20、迭代器(Iterator)模式
提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。 - 21、访问者(Visitor)模式
在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象。 - 22、备忘录(Memento)模式
在不破坏封装的前提下,获取并保存一个对象的内部状态,以便以后恢复它。 - 23、解释器(Interpreter)模式
提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。
注:这二十三种设计模式并不是孤立存在的,很多模式之间存在一定的关联关系。在大型系统的开发中常常同时用到多种设计模式。
创建型设计模式
创建型设计模式的主要作用是 “怎么创建对象”,主要特点是 “将对象的创建和使用分离”。这样可以降低系统的耦合度。
创建型设计模式主要分为以下五种:
- 1、单例(Singleton)模式
某个类只能生成一个实例,且该类提供了一个全局访问点,供外部获取该实例。其扩展是有限多个实例。 - 2、原型(Prototype)模式
将一个对象作为原型,通过对其复制而克隆出多个与原型类似的新实例。 - 3、工厂方法(Factory Method)模式
定义一个用户创建产品的接口,由子类决定生产什么产品。 - 4、抽象工厂(Abstract Factory)模式
提供一个创建产品族的接口,其每个子类可以生产一系列相关产品。 - 5、建造者(Builder)模式
将一个复杂的对象分解成多个简单的部分,然后根据不同需要分别创建它们,最后把它们构建成复杂的对象。
1、单例(Singleton)模式
某个类只能生成一个实例,且该类提供了一个全局访问点,供外部获取该实例。其扩展是有限多个实例。单例模式是最简单的设计模式之一。其构造函数是私有的,即外部类不能通过 new 关键字来创建单例对象;单例类需定义一个静态私有类变量,然后对外提供一个共有函数来实例化此类变量(即实例),然后返回该实例。
单例模式有七种实现方式,分别是:懒汉(线程不安全)、懒汉(线程安全)、饿汉、饿汉-变种、静态内部类、枚举、双重校验锁。
其特点是:
- 单例类只有一个对象。
- 该单例对象只能由该单例类自行创建(构造函数私有化)。
- 单例类必须对外提供一个全局访问点。
单例模式实现:
-
懒汉(线程不安全)
public class Singleton { private static Singleton singleton; private Singleton() {} public static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
-
懒汉(线程安全)
public class Singleton { private static Singleton singleton; private Singleton() {} public static synchronized Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
-
饿汉
public class Singleton { private static Singleton singleton = new Singleton(); private Singleton() {} public static Singleton getInstance() { return singleton; } }
-
饿汉-变种
public class Singleton { private static Singleton singleton; static { singleton = new Singleton(); } private Singleton() {} public static Singleton getInstance() { return singleton; } }
-
静态内部类
public class Singleton { private static class SingletonHandler { private static final Singleton INSTANCE = new Singleton(); } private Singleton() {} public static Singleton getInstance() { return SingletonHandler.INSTANCE; } }
-
枚举
public enum Singleton { INSTANCE; Singleton() {} }
-
双重校验锁
public class Singleton { private static Singleton singleton; private Singleton() {} public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
// test
Singleton singleton = Singleton.getInstance();
2、原型(Prototype)模式
将一个对象作为原型,通过对其复制而克隆出多个与原型类似的新实例。
使用原型模式要比使用 new 关键字创建对象在性能上好得多。因为 Object 的 clone() 方法是一个本地方法,它直接操作内存中的二进制流,特别是大对象时,性能的差异特别明显。使用原型模式就好比复制粘贴,而不是亲手码。因此,当需要频繁的创建相似对象的时候可以考虑使用原型模式,比如在一个循环内创建对象。
在实际应用中,原型模式很少单独出现,而是与其它模式混合使用。原型模式的实现也非常简单,创建一个原型类,实现一个方法即可。这个类常用抽象类来代替。
原型类需要实现以下功能:
- 实现 Cloneable 接口:
在 java 中,只有实现了 java.lang.Cloneable 接口的类才可被拷贝,否则会抛出 ConeNotSupportedException 异常。 - 重写 clone() 方法:
Object 类中的 clone() 方法,作用是返回当前对象的一个拷贝,但其作用域是 protected,一般类无法调用,所以,原型类需要重写 clone() 方法,将其作用域修改为 public。
原型模式实现:
// 原型类 实现 Cloneable 接口
public class Prototype implements Cloneable {
public Prototype() {}
// 重写 clone() 方法
public Prototype clone() {
Prototype prototype = null;
try {
prototype = (Prototype) super.clone();
// 针对数组、容器、引用数据类型进行深拷贝
// prototype.list = (ArrayList) this.list.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return prototype;
}
}
// 原型对象
Prototype prototype = new Prototype();
// 通过原型对象克隆出来的新实例
Prototype prototype1 = prototype.clone();
原型模式使用注意事项:
- 深拷贝与浅拷贝:
Object 类中的 clone() 只会拷贝对象中的基本数据类型,属于浅拷贝。对于数组、容器、引用数据类型等不会拷贝,因此需要自己实现深拷贝。
3、工厂方法(Factory Method)模式
定义一个用户创建产品的接口,由子类决定生产什么产品。
被创建的对象(用户需要的对象)称为产品,创建产品的对象称为工厂。如果要创建的产品只有一种,则只需要一个工厂类即可,这种模式叫做 “简单工厂模式”,它不属于 23 种经典设计模式,它的缺点是增加新的产品时会违背 “开闭原则”。工厂方法模式是对简单工厂模式的抽象,其好处是系统在不需要修改原代码的情况下可以引进新的产品,即满足 “开闭原则”。
工厂方法模式的优缺点:
- 优点:
- 用户只需要直到具体工厂就可得到所需的产品,无需了解产品的创建过程。
- 新增产品时,只需要添加具体的产品类和其对应的具体工厂类即可,无需对原工厂和其它产品类、工厂类修改。
- 缺点:
- 每增加一个新产品都要增加对应的产品类和工厂类,如果产品过多则会使系统过于复杂。
工厂方法模式的主要组件:
- 抽象产品(Abstract Product):定义产品的行为。
- 具体产品(Specific Product):实现抽象产品的行为使抽象产品具象化。
- 抽象工厂(Abstract Factory):定义生产产品的方法。
- 具体工厂(Specific Factory):实现抽象工厂中生产产品的方法,来决定具体生产那一个产品。
工厂方法模式的实现:
// 抽象产品类
public interface AbstractProduct {
// 抽象产品行为
void behavior();
}
// 具体产品一
public class ProductOne implements AbstractProduct {
@Override
public void behavior() {
sout("product one behaviro");
}
}
// 具体产品二
public class ProductTwo implements AbstractProduct {
@Override
public void behavior() {
sout("product two behaviro");
}
}
// 抽象工厂类
public interface AbstractFactory {
// 生产产品的方法
AbstractProduct product();
}
// 产品一的具体生产类
public class ProductOneFactory implements AbstractFactory {
// 生产产品一的具体方法
@Override
public AbstractProduct product() {
return new ProductOne();
}
}
// 产品二的具体生产类
public class ProductTwoFactory implements AbstractFactory {
// 生产产品二的具体方法
@Override
public AbstractProduct product() {
return new ProductTwo();
}
}
// 获得生产产品一的工厂对象
ProductOneFactory factory = new ProductOneFactory();
// 通过 product() 生产产品一
AbstractProduct product = factory.product();
product.behaviro();
4、抽象工厂(Abstract Factory)模式
提供一个可以创建产品族的接口,每个子类可以创建一系列相关产品。
抽象工厂是工厂方法的升级版,工厂方法只能生产一个等级的产品,而抽象工厂可以生产多个等级的产品。
抽象工厂模式一般满足以下条件:
- 系统中有多个产品族,每个具体工厂生产属于同一族但不同等级结构的产品。
- 系统一次只可消费其中某一族的产品,即同族的产品才可一起被消费。
抽象工厂模式的优缺点:
- 优点:
- 可以在类内部对产品族中相关联的多等级的产品共同管理,而不必增加新的类来管理。
- 增加一个新的产品族时不需要修改原代码,只需要增加新的多个属于某个等级的具体产品,然后增加这个产品族的具体工厂即可。满足 “开闭原则”。
- 缺点:
- 当产品族中增加一个新产品时,几乎所有的工厂类都需要修改,所以产品的等级结构划分是非常重要的。
抽象工厂模式的组件:
- 抽象产品(Abstract Product):定义产品的规范,即产品等级。存在多个抽象产品,即多个等级的抽象产品。
- 具体产品(Specific Product):实现了抽象产品的接口,是产品具象化。具体产品与具体工厂是多对一的关系。
- 抽象工厂(Abstract Factory):提供了多个生产产品的方法,每个方法可以生产多个同等级的产品。
- 具体工厂(Specific Factory):实现了抽象工厂中的多个方法,每次生产出来的商品属于同一族但不同等级的。
抽象工厂模式的实现:
下面的示例中有两大族产品,分别是 HuaWei 和 Apple;有两个等级结构,分别是 mp(手机)和 pc(电脑)。
// 手机等级的抽象产品类
public interface MP {
void properties();
}
// 电脑等级的抽象产品类
public interface PC {
void properties();
}
// 属于 HuaWei 族 且属于 MP 等级结构的产品
public class HuaWeiMP implements MP {
@Override
public void properties() {
sout("I'm a huawei mp")
}
}
// 属于 Apple 族 且属于 MP 等级结构的产品
public class AppleMP implements MP {
@Override
public void properties() {
sout("I'm a apple mp")
}
}
// 属于 HuaWei 族 且属于 PC 等级结构的产品
public class HuaWeiPC implements PC {
@Override
public void properties() {
sout("I'm a huawei pc")
}
}
// 属于 Apple 族 且属于 PC 等级结构的产品
public class ApplePC implements PC {
@Override
public void properties() {
sout("I'm a apple pc")
}
}
// 生产 MP 和 PC 等级结构产品的抽象类
public interface Factory {
MP productMp();
PC productPC();
}
// 生产 HuaWei 族产品的具体工厂
public class HuaWeiFactory implements Factory {
@Override
public MP productMp() {
return new HuaWeiMP();
}
@Override
public PC productPC() {
return new HuaWeiPC();
}
}
// 生产 Apple 族产品的具体工厂
public class AppleFactory implements Factory {
@Override
public MP productMp() {
return new AppleMP();
}
@Override
public PC productPC() {
return new ApplePC();
}
}
// test 开始生产 HuaWei 族的产品
HuaWeiFactory huawei = new HuaWeiFactory();
huawei.productMP();
huawei.productPC();
5、建造者(Builder)模式
将一个复杂的对象分解成多个简单的部分,然后根据不同需要分别创建它们,最后把它们构建成复杂的对象。
特点是将对象的构造与它的标识分离,使同样的构建过程可以创建不同的表示。它将变与不变分离,即产品的组成部分是不变的,但每一部分雀氏可以灵活选择的。
建造者模式的优缺点:
- 优点:
- 各个建造者相互独立,有利于系统的扩展。
- 客户端不知道产品的内部组成细节,降低风险。
- 缺点:
- 产品的组成部分必须相同,一定程度上限制了其使用范围。
- 如果产品的构成部分发生变化,则该模式会增加很多具体建造者类。
建造者模式的组件:
- 产品(Product):它是包含多个简单部分的复杂对象,由具体建造者来创建各个简单部分。
- 抽象建造者(Abstract Builder):它是一个包含创建各个简单部分和返回复杂产品的方法的接口。
- 具体建造者(Specific Builder):实现抽象建造者接口,实现其创建各个简单部分和返回复杂产品的方法。
- 指挥者(Director):它调用建造者中的创建简单部分和构建复杂产品的方法,在指挥者中不涉及具体产品信息。
建造者模式的实现:
// 产品 包含 partA partb partC 三个简单部分
public class Product {
private String partA;
private String partB;
private String partC;
public void setPartA(String partA) {
this.partA = partA;
}
public void setPartB(String partB) {
this.partB = partB;
}
public void setPartC(String partC) {
this.partC = partC;
}
}
// 抽象建造者
public abstract class Builder {
abstract void buildPartA(); // 创建简单部分 partA 的方法
abstract void buildPartB();
abstract void buildPartC();
abstract Product getResult(); // 返回最终构建的复杂对象的方法
}
// 具体建造者 实现抽象建造者中的方法
public class SpecificBuilder extends Builder {
private Product product = new Product();
@Override
void buildPartA() {
product.setPartA("partA");
}
@Override
void buildPartB() {
product.setPartB("partB");
}
@Override
void buildPartC() {
product.setPartC("partC");
}
@Override
Product getResult() {
return product;
}
}
// 指挥者
public class Director {
private final Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public Product build() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
// test
Builder builder = new SpecificBuilder();
Director director = new Director(builder);
Product product = director.build();
System.out.println(product.toString());
结构型设计模式
用于描述 ”如何将类或对象按照某种布局组成更大的结构“。它分为类结构型设计模式和对象结构型设计模式,类结构型设计模式采用继承来组合接口和类,对象结构型设计模式采用组合与聚合来组合对象。
由于组合关系和聚合关系较继承关系耦合度低,满足 ”合成复合原则“,所以对象结构型设计模式比类结构型设计模式更具灵活性。
结构型设计模式主要分为以下七种:
- 1、代理(Proxy)模式
为某个对象提供一种代理以控制对对象的访问。即客户端通过代理间接的访问该对象,从而限制、增强、修改该对象的一些特性。 - 2、适配器(Adapter)模式
将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能在一起工作的来能够在一起工作。 - 3、桥接(Bridge)模式
将抽象与实现分离,使它们可以独立变化。它是用组合代替继承实现,从而降低 ”抽象与实现“ 这两个可变维度的耦合度。 - 4、装饰(Decorator)模式
动态的给对象增加一些职责,即增强对象的额外功能。 - 5、外观(Facade)模式
为多个复杂的子系统提供一个统一的接口,使得这些子系统更加容易被访问。 - 6、享元(Flyweight)模式
运用共享技术来有效的支持大量细粒度对象的使用。 - 7、组合(Composite)模式
将对象组合成树状层次结构,是用户对单个对象和组合对象有一致的访问性。
1、代理(Proxy)模式
为某个对象提供一种代理以控制对对象的访问。即客户端通过代理间接的访问对象,从而限制、增强、修改对象的一些特性。访问者不想或不能直接访问目标对象,代理对象作为目标对象和访问者之间的中介。
代理模式的优缺点:
- 优点:
- 代理对象在目标对象和访问者之间起到一个中介的作用,且可以保护目标对象。
- 代理模式可以扩展目标对象的功能。
- 代理模式将访问者与目标对象分离,在一定程度上降低了系统的耦合度。
- 缺点:
- 在目标对象与访问者之间增加的代理对象会使请求变慢。
- 增加代理对象增加了系统的复杂度。
代理模式的组件:
- 抽象目标类(Abstract Target):声明目标类的行为。
- 具体目标类(Specific Target):实现抽象目标类的方法以确定目标对象的功能。
- 代理类(Proxy):提供了与目标类相同的接口,其内部包含对具体目标的引用,且可以扩展具体目标的功能。
代理模式的实现:
// 抽象目标类 定义目标的行为
public interface Target {
void request();
}
// 具体目标类 实现抽象目标的行为
public class SpecificTarget implements Target {
public SpecficTarget() {}
@Override
public void request() {
sout("I'm a specfic target");
}
}
// 代理类 实现抽象目标类的行为 并引用具体目标对象 扩展其功能
public class Proxy implements Target {
private SpecficTarget specficTarget;
@Override
public void request() {
if (specficTarget == null) {
specficTarget = new SpecifcTarget();
}
sout("前置处理");
specficTarget.request();
sout("后置处理");
}
}
// test
Target proxy = new Proxy();
proxy.request();
2、适配器(Adapter)模式
将一个类的某个接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能在一起工作的类可以在一起工作。
适配器模式分为类适配器模式和对象适配器模式,前者比后者耦合度高。
适配器模式的优缺点:
- 优点:
- 客户端通过适配器可以透明的调用目标接口。
- 复用了现存的组件,即不用修改已有的具体目标接口即可实现对具体目标接口的调用。
- 缺点:
- 对适配器来说,更换适配器比较复杂。
适配器模式的组件:
- 抽象目标接口(Abstract Target):即用户希望的接口。
- 具体目标接口(Specific Target):即用户实际要访问的接口。
- 适配器(Adapter):通过引用或继承具体目标接口以及实现用户希望的接口,将具体目标接口转换成用户希望的接口。
类适配器模式的实现:
// 抽象目标接口 即用户希望的接口
public interface Target {
void request();
}
// 具体目标接口 即用户实际想访问的接口
public class SpecificTarget {
public void specificRequest() {
sout("I'm a specific target");
}
}
// 类适配器
public class ClassAdapter extends SpecificTarget implements Target {
@Override
public void request() {
specificRequest();
}
}
// test
Target adapter = new ClassAdapter();
adapter.request();
对象适配器模式的实现:
// 对象适配器
public class ObjectAdapter implements Target {
private SpecificTarget specificTarget;
public ObjectAdapter (SpecificTarget specificTarget) {
this.specificTarget = specificTarget;
}
@Override
public void request() {
specificTarget.specificRequest();
}
}
// test
SpecificTarget specificTarget = new SpecificTarget;
Target adapter = new ObjectAdapter(specificTarget);
adapter.request();
3、桥接(Bridge)模式
将抽象与实现分离,使它们可以独立变化,它是用组合代替继承实现,从而降低 “抽象和实现” 这两个可变维度的耦合度。对于两个独立变化的维度,使用桥接模式再适合不过了。
桥接模式的优缺点:
- 优点:
- 抽象与实现的分离。
- 优秀的扩展能力。
- 实现细节对客户透明。
- 缺点:
- 桥接模式的引入会增加系统的理解和设计难度,由于聚合关系建立在抽象层,要求开发者针对抽象层进行设计与编程。
桥接模式的组件:
- 实现化接口:定义实现化角色接口,供抽象化角色聚合使用。
- 具体实现化接口:实现实现化接口。
- 抽象化角色:定义抽象类,聚合实现化接口。
- 扩展抽象化角色:抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化接口方法。
桥接模式的实现:
// 桥接接口 实现化接口
public interface UseR {
void useR(String name);
}
// 实现化接口实现类
public class ZedUseR implements UseR {
@Override
public void useR(String name) {
sout(name);
}
}
// 实现化接口实现类
public class FizzUseR implements UseR {
@Override
public void useR(String name) {
sout(name);
}
}
// 抽象化角色
public abstract class Hero {
protected UseR useR;
protected Hero(UseR useR) {
this.useR = useR;
}
abstract void useR();
}
// 抽象化角色的实现 实现其业务方法
public class ZhaoHuanShi extends Hero {
private String name;
public ZhaoHuanShi(String name, UseR useR) {
super(useR);
this.name = name;
}
@Override
public void useR() {
useR.userR(name);
}
}
// test
ZhaoHaunShi guo = new ZhaoHuanShi("瞬狱影杀阵", new ZedUseR());
guo.useR();
ZhaoHaunShi guo1 = new ZhaoHuanShi("巨鲨强袭", new FizzUseR());
guo1.useR();
4、装饰(Decorator)模式
动态的给对象增加一些职责,及增强对象的功能。在不想增加子类的情况下扩展类可使用装饰者模式。
装饰模式优缺点:
- 优点:
- 装饰类和被装饰类可以独立发展,互不耦合。
- 代替继承模式,动态扩展一个类的功能。
- 缺点:
- 多层装饰比较复杂。
装饰模式的组件:
- 抽象接口:规范装饰类和被装饰类的基本行为。
- 被装饰类:实现抽象接口,实现其基本业务方法。
- 抽象装饰器类:实现抽象接口,构造引用被装饰类以拥有其原有功能职责。
- 具体装饰器类:继承抽象装饰器类,以增加新的职责和能力。
装饰模式的实现:
// 抽象接口 规范装饰器和被装饰类的基本行为
public interface Hero {
void attack();
}
// 被装饰类一
public class ZedHero implements Hero {
@Override
public void attack() {
System.out.println("R-F-W-E-Q-R-Ctrl+6");
}
}
// 被装饰类二
public class FizzHero implements Hero {
@Override
public void attack() {
System.out.println("Q-R-E-D-F-W-Ctrl+6");
}
}
// 抽象装饰器
public abstract class HeroDecorator implements Hero {
protected Hero hero;
public HeroDecorator(Hero hero) {
this.hero = hero;
}
@Override
public void attack() {
hero.attack();
}
}
// 具体装饰器一
public class ZedHeroDecorator extends HeroDecorator {
private String equipment;
public ZedHeroDecorator(Hero hero, String equipment) {
super(hero);
this.equipment = equipment;
}
@Override
public void attack() {
// 新增职责
sout(equipment);
hero.attack();
}
}
// 具体装饰器二
public class FizzHeroDecorator extends HeroDecorator {
private String equipment;
public FizzHeroDecorator(Hero hero, String equipment) {
super(hero);
this.equipment = equipment;
}
@Override
public void attack() {
// 新增职责
sout(equipment);
hero.attack();
}
}
// test
// zed 与 fizz 对象被装饰前
ZedHero zedHero = new ZedHero();
zedHero.attack();
FizzHero fizzHero = new FizzHero();
fizzHero.attack();
// zed 与 fizz 对象被装饰后
ZedHeroDecorator zedHeroDecorator = new ZedHeroDecorator(new ZedHero(),
"水银之靴、星蚀、塞瑞尔达的怨恨、收集者、幽梦之灵、守护天使");
zedHeroDecorator.attack();
FizzHeroDecorator fizzHeroDecorator = new FizzHeroDecorator(new FizzHero(),
"明朗之靴、暗夜收割者、中娅沙漏、巫妖之祸、灭世者的死亡之帽、莫雷洛秘典");
fizzHeroDecorator.attack();
5、外观(Facade)模式
为多个复杂的子系统提供一个统一的接口,使得这些子系统更加容易被访问。在现有的系统中添加一个接口,提供了客户端请求的简化方法和现有系统类方法的委托调用,同时隐藏了系统的复杂性。
外观模式的优缺点:
- 优点:
- 减少系统的相互依赖。
- 提高灵活性。
- 提供安全性。
- 缺点:
- 增加新的子系统可能需要修改外观接口,不符合 “开闭原则”。
外观模式的组件:
- 子系统抽象接口:用来规范子系统,可有可无。
- 子系统:实现子系统所体现的功能,有多个。
- 外观接口:向客户端提供访问系统的简化方法以及对现有子系统的委托调用。
外观模式的实现:
// 子系统抽象接口
public interface Hero {
void attack();
}
// 子系统一
public class Zed implements Hero {
@Override
public void attack() {
System.out.println("瞬狱影杀阵");
}
}
// 子系统二
public class Fizz implements Hero {
@Override
public void attack() {
System.out.println("巨鲨强袭");
}
}
// 子系统三
public class Ahri implements Hero {
@Override
public void attack() {
System.out.println("灵魂突袭");
}
}
// 外观接口
public class Facade {
private final Hero zed;
private final Hero fizz;
private final Hero ahri;
public Facade() {
this.zed = new Zed();
this.fizz = new Fizz();
this.ahri = new Ahri();
}
public void attack() {
zed.attack();
fizz.attack();
ahri.attack();
}
}
// test
Facade facade = new Facade();
facade.attack();
6、享元(Flyweight)模式
运用共享技术来有效的支持大量细粒度对象的使用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量,避免大量相似类的开销。
享元模式存在两种状态:内部状态,即不会随着环境而改变的共享部分;外部状态,即随着环境而改变的不可共享部分。享元模式的实现要领就是区分应用中这两种状态,并将外部状态外部化。
享元模式的优缺点:
- 优点:
- 减少相似对象的创建,降低系统内存的开销,提高效率。
- 缺点:
- 提高了系统的复杂度,需要分离出内部状态和外部状态,内部状态不变,外部状态可变,容易造成系统混乱。
享元模式的组件:
- 抽象享元接口:为具体享元类规范接口,外部状态以参数方式传入。
- 具体享元:实现抽象享元接口中的参数。
- 非享元角色:外部状态,以参数的形式注入到享元中去。
- 享元工厂:负责创建和管理享元对象。当客户请求一个享元对象时,享元工厂先检查系统中是否已存在符合要求的享元对象,若存在则返回,否则则创建一个返回。
享元模式的实现:
// 抽象享元接口
public interface Hero {
// name 为外部状态
void select(String name);
}
// 具体享元
public class SpecificHero implements Hero {
private String type;
public SpecificHero(String type) {
this.type = type;
}
@Override
public void select(String name) {
sout(type + " " + name);
}
}
// 享元工厂
public class HeroFactory {
private static final Map<String, Hero> map = new HashMap<>();
public static Hero getHero(String type) {
SpecificHero specificHero = (SpecificHero) map.get(type);
if (specificHero == null) {
System.out.println("共享状态不存在 创建 " + type);
specificHero = new SpecificHero(type);
map.put(type, specificHero);
}
return specificHero;
}
}
// test
Hero hero = HeroFactory.getHero("MID");
hero.select("ZED");
hero.select("FIZZ");
hero.select("AHRI");
// console
共享状态不存在 创建 MID
MID ZED
MID FIZZ
MID AHRI
7、组合(Composite)模式
将对象组合成树状层次结构,使用户对单个对象和对组合对象有一致的访问性。
在树型结构问题中,模糊了简单元素和复杂元素的概念,使客户端可以像处理简单元素一样处理复杂元素,从而使客户端与复杂元素的内部结构解耦。
组合模式的优缺点:
- 优点:
- 高层模块调用简单。
- 节点自由增加。满足开闭原则。
- 缺点:
- 在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了 依赖倒置原则。
组合模式的组件:
- 抽象节点(AbstractNode):主要作用是声明树枝节点和树叶节点的公共接口,并实它们的默认行为。在透明组合模式中,还需要声明管理子节点的方法;在安全组合模式中,不需要声明管理子节点的方法,而是交给树枝节点管理。
- 树枝节点(BranchNode):是组合模式中的分支节点对象,存在子节点,实现了抽象节点中的公共接口。主要作用是存储和管理叶子节点,通常有 add()、remove()、getChild() 方法。
- 树叶节点(LeafNode):是组合模式中的叶子节点对象,没有子节点,实现了抽象节点中的公共接口。
组合模式的实现:
-
透明组合模式:
// 抽象节点 public interface AbstractNode { void add(AbstractNode abstractNode); void remove(AbstractNode abstractNode); AbstractNode getChild(int i); void attack(); }
// 树枝节点 public class BranchNode implements AbstractNode { private final List<AbstractNode> childList = new ArrayList<>(); @Override public void add(AbstractNode abstractNode) { childList.add(abstractNode); } @Override public void remove(AbstractNode abstractNode) { childList.remove(abstractNode); } @Override public AbstractNode getChild(int i) { return childList.get(i); } @Override public void attack() { for (AbstractNode node : childList) { node.attack(); } } }
// 树叶节点 public class LeafNode implements AbstractNode { private String name; public LeafNode(String name) { this.name = name; } @Override public void add(AbstractNode abstractNode) {} @Override public void remove(AbstractNode abstractNode) {} @Override public AbstractNode getChild(int i) { return null; } @Override public void attack() { System.out.println("叶子节点攻击 " + name); } }
// composite transparent test AbstractNode place = new BranchNode(); AbstractNode mid = new BranchNode(); AbstractNode top = new BranchNode(); AbstractNode zed = new LeafNode("禁奥义·瞬狱影杀阵"); AbstractNode fizz = new LeafNode("巨鲨来袭"); AbstractNode riven = new LeafNode("放逐之锋"); AbstractNode irelia = new LeafNode("先锋之刃"); mid.add(zed); mid.add(fizz); top.add(riven); top.add(irelia); place.add(mid); place.add(top); place.attack();
-
安全组合模式实现:
// 抽象节点 public interface AbstractNode { void attack(); }
// 树枝节点 public class BranchNode implements AbstractNode { private final List<AbstractNode> childList = new ArrayList<>(); public void add(AbstractNode abstractNode) { childList.add(abstractNode); } public void remove(AbstractNode abstractNode) { childList.remove(abstractNode); } public AbstractNode getChild(int i) { return childList.get(i); } @Override public void attack() { for (AbstractNode node : childList) { node.attack(); } } }
// 叶子节点 public class LeafNode implements AbstractNode { private String name; public LeafNode(String name) { this.name = name; } @Override public void attack() { System.out.println("叶子节点攻击 " + name); } }
// composite security test BranchNode place = new BranchNode(); BranchNode mid = new BranchNode(); BranchNode top = new BranchNode(); AbstractNode zed = new LeafNode("禁奥义·瞬狱影杀阵"); AbstractNode fizz = new LeafNode("巨鲨来袭"); AbstractNode riven = new LeafNode("放逐之锋"); AbstractNode irelia = new LeafNode("先锋之刃"); mid.add(zed); mid.add(fizz); top.add(riven); top.add(irelia); place.add(mid); place.add(top); place.attack();
行为型设计模式
用来描述 “类或对象之间怎样协作来完成单个对象无法单独单程的任务,以及怎样分配职责”。
行为型设计模式主要分为以下十一种:
- 1、模板方法(Template Method)模式
定义一个操作中的算法骨架,而将一些步骤延迟到子类中,使得算法可以在不改变算法结构的情况下改变算法某个特点步骤。 - 2、策略(Strategy)模式
定义一系列算法,并将每个算法封装起来,使得它们可以相互替换,且算法的改变不影响用户的使用。 - 3、命令(Command)模式
将一个请求封装成一个对象,使得发出请求的责任和执行请求的责任分开。 - 4、职责链(Chain Of Responsibility)模式
把请求从链中的一个对象传到下一个对象,知道请求被响应位置。通过这种方式来去除对象之间的耦合。 - 5、状态(State)模式
允许一个对象在其内部状态发生改变时改变其行为能力。 - 6、观察者(Observer)模式
多个对象之间存在一对多的关系,当一个对象发生改变时,将这种改变通知给其它对象,从而影响其它对象的行为能力。 - 7、中介者(Mediator)模式
定义一个中介者来简化多个原有对象之间的关系,使得原有对象之间不必相互了解,从而降低系统耦合度。 - 8、迭代器(Iterator)模式
提供一种方法来顺序访问聚合对象中一系列数据,而不暴露聚合对象的内部表示。 - 9、访问者(Visitor)模式
在不改变集合元素的前提下,为集合中的每个元素提供多种访问方式。即集中中的每个元素都有多个访问者对象。 - 10、备忘录(Memento)模式
在不破坏封装的前提下,获取并保存一个对象的内部状态,以便以后回复它。 - 11、解释器(Interpreter)模式
提供如何定义语言的文法,以及语言句子的解释方法。
1、模板方法(Template Method)模式
定义一个算法骨架,而将一些步骤延迟到子类的中,使得子类可以在不改变算法骨架的情况下重新定义该算法的某些特定步骤。
其使用场景为:有多个子类共有的方法,且逻辑相同;重要的、复杂的方法可以考虑作为模板方法。
模板方法模式的优缺点:
- 优点:
- 封装不变的部分,扩展可变的部分。
- 提取公共部分,便于维护。
- 行为由父类声明,子类实现。
- 缺点:
- 每一个不同的步骤都需要一个子类来实现,会导致类的个数增加,使系统变得庞大复杂。
模板方法模式的组件:
- 抽象接口:定义不变的部分,即提取出来的功能代码;声明子类的行为。
- 具体实现:继承抽象接口,实现父类中声明的子类行为。
模板方法模式的实现:
public abstract class AbstractGame {
// 声明子类行为 逻辑相同 但具体实现不同
abstract void initGame();
abstract void startGame();
abstract void endGame();
// 封装不变、公共代码 定义为 final 型 防止被改变
public final void playGame() {
initGame();
startGame();
endGame();
}
}
// 具体实现
public class LOLGame extends AbstractGame {
@Override
void initGame() {
System.out.println("匹配对手并选择英雄");
}
@Override
void startGame() {
System.out.println("对线");
}
@Override
void endGame() {
System.out.println("Ctrl + 6");
}
}
// 具体实现
public class DotaGame extends AbstractGame {
@Override
void initGame() {
System.out.println("选择人机并选择英雄");
}
@Override
void startGame() {
System.out.println("对线");
}
@Override
void endGame() {
System.out.println("Ctrl + 6");
}
}
// template method test
AbstractGame lol = new LOLGame();
lol.playGame();
AbstractGame dota = new DotaGame();
dota.playGame();
2、策略(Strategy)模式
定义一系列算法,并将每个算法封装起来,使其可以相互替换,且算法的改变不影响用户的使用。
策略模式的优缺点:
- 优点:
- 算法(策略)可以自由切换。
- 避免使用多重条件判断。
- 扩展性良好。
- 缺点:
- 策略类会增多,导致系统膨胀。
- 所有策略类都需对外暴露。
注:当一个系统的策略类多余四个时,就需要考虑使用混合模式,解决策略类膨胀的问题。
策略模式的组件:
- 抽象接口:声明所有算法的行为接口。
- 具体实现:实现抽象接口,实现其具体行为。
- 用户:算法的使用者。
策略模式的实现:
// 抽象接口
public interface Hero {
void equipment();
}
// 实现一
public class ZedEquipmentOne implements Hero {
@Override
public void equipment() {
System.out.println("水银之靴、星蚀、死亡之舞、玛莫提乌斯之噬、亡者的板甲、守护天使");
}
}
// 实现二
public class ZedEquipmentTwo implements Hero {
@Override
public void equipment() {
System.out.println("明朗之靴、德拉克萨的幕刃、收集者、赛睿尔达的怨恨、幽梦之灵、守护天使");
}
}
// 用户
public class MidPlayer {
private Hero hero;
public MidPlayer(Hero hero) {
this.hero = hero;
}
public void play() {
hero.equipment();
}
}
// strategy test
MidPlayer player1 = new MidPlayer(new ZedEquipmentOne());
player1.play();
MidPlayer player2 = new MidPlayer(new ZedEquipmentTwo());
player2.play();
3、命令(Command)模式
将一个请求的封装为一个对象,使得请求的接收责任和实现责任分开。即接收者与实现者解耦。
如顾客吃饭,普通模式下是顾客与厨师直接交流,此时厨师即下单又做菜;命令模式下则是顾客与服务员交流,服务员负责下单,厨师只负责做菜。即顾客点菜、服务员下单、厨师做菜。
命令模式的优缺点:
- 优点:
- 降低了系统的耦合度。
- 新的命令可以很容易添加到系统中。
- 缺点:
- 若命令过多,则具体的命令调用者类将增多,使系统膨胀。
命令模式的组件:
- 命令:负责执行具体的命令。
- 命令发起者(调用者):负责发起(调用)某个命令,与具体的命令一一相对。
- 命令接收者:负责接受命令。
命令模式的实现:
// 命令
public class Command {
public void q() {
System.out.println("影奥义·诸刃");
}
public void w() {
System.out.println("影奥义·分身");
}
public void e() {
System.out.println("影奥义·鬼斩");
}
public void r() {
System.out.println("禁奥义·瞬狱影杀阵");
}
}
// 命令发送者 及其实现类
public interface Sender {
void sender();
}
public class QSender implements Sender {
private Command command;
public QSender(Command command) {
this.command = command;
}
@Override
public void sender() {
command.q();
}
}
public class WSender implements Sender {
private Command command;
public WSender(Command command) {
this.command = command;
}
@Override
public void sender() {
command.w();
}
}
public class ESender implements Sender {
private Command command;
public ESender(Command command) {
this.command = command;
}
@Override
public void sender() {
command.e();
}
}
public class RSender implements Sender {
private Command command;
public RSender(Command command) {
this.command = command;
}
@Override
public void sender() {
command.r();
}
}
// 命令接收者
public class Invoker {
private final List<Sender> list = new ArrayList<>();
public Invoker() {}
public void addSender(Sender sender) {
list.add(sender);
}
public void invoker() {
for (Sender sender : list) {
sender.sender();
}
}
}
// command test
// 命令
Command command = new Command();
// 命令发送者(请求接收者)
Sender qSender = new QSender(command);
Sender wSender = new WSender(command);
Sender eSender = new ESender(command);
Sender rSender = new RSender(command);
// 调用者(请求发起者)
Invoker invoker = new Invoker();
invoker.addSender(qSender);
invoker.addSender(wSender);
invoker.addSender(eSender);
invoker.addSender(rSender);
// 发起请求
invoker.invoker();
4、职责链(Chain Of Responsibility)模式
把请求从链中的一个对象传到下一个对象,直到请求被响应为之。通过这种方式来解耦。
为了避免请求发送者和多个请求处理者耦合,通过前一个处理者对下一个处理者的引用而形成请求处理链,当请求发生时将沿着链向下传递,直到被处理为止。
职责链模式的优缺点:
- 优点:
- 降低了对象之间的耦合度。即降低了请求发送者和请求处理者之间的耦合度;降低了每个处理类之间的耦合度。
- 增加了系统的可扩展性。若请求逻辑有变动只需要修改对应的处理类即可。
- 明确了处理的功能。即使类的职责单一化。
- 缺点:
- 不能保证每一个请求都能被处理。因为请求没有明确的的接收者,只有入口,可能直到链尾也没有处理类来处理请求。
- 若请求处理链过长,则会影响系统性能。
- 职责链建立的合理性需要客户端来保证,增加了客户端的复杂性。
职责链模式的组件:
- 抽象处理器:声明处理方法,定义并管理下一个处理器节点。
- 具体处理器:实现抽象处理器接口,实现其处理方法。
- 客户端:创建处理链,并发起请求。
职责链模式的实现:
// 抽象处理器
public abstract class Handler {
private Handler next;
public Handler getNext() {
return next;
}
public void setNext(Handler next) {
this.next = next;
}
abstract void handler(String params);
}
// 具体处理器一
public class OneHandler extends Handler {
@Override
void handler(String params) {
if ("one".equals(params)) {
System.out.println("处理器一负责处理该请求");
return;
}
if (getNext() == null) {
System.out.println("无处理器处理该请求");
return;
}
getNext().handler(params);
}
}
// 具体处理器二
public class TwoHandler extends Handler {
@Override
void handler(String params) {
if ("two".equals(params)) {
System.out.println("处理器二负责处理该请求");
return;
}
if (getNext() == null) {
System.out.println("无处理器处理该请求");
return;
}
getNext().handler(params);
}
}
// chain of responsitility test/client
Handler handler1 = new OneHandler();
Handler handler2 = new TwoHandler();
handler1.setNext(handler2);
handler1.handler("one");
handler1.handler("two");
5、状态(State)模式
允许一个对象在其内部状态发生改变时改变其行为能力。
对有状态的对象,把每个状态及其对应的行为能力封装到具体的状态类中。
状态模式的优缺点:
- 优点:
- 满足单一职责原则,即把每个状态对应的行为能力封装到了具体的状态类中。
- 将状态的转化显示化,减少对象之间的相互依赖。
- 缺点:
- 状态的增多必然会导致状态类增多,从而使系统膨胀。
- 对开闭原则的支持不太友好,新状态的增加需要修改负责状态转换的代码。
状态模式的组件:
- 状态上下文:定义客户端接口,维护当前状态。
- 抽象状态:声明状态处理接口,即规范具体状态的行为。
- 具体状态:实现抽象状态,实现其状态处理接口,即实现当前状态对应的行为,并在需要的情况下切换状态。
状态模式的实现:
// 状态上下文
public class Context {
private State state;
public Context() {
this.state = new OneState();
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public void handler() {
state.handler(this);
}
}
// 抽象状态
public abstract class State {
abstract void handler(Context context);
}
// 具体状态一
public class OneState extends State {
@Override
void handler(Context context) {
System.out.println("当前状态 one 及其行为");
context.setState(new TwoState());
}
}
// 具体状态二
public class TwoState extends State {
@Override
void handler(Context context) {
System.out.println("当前状态 two 及其行为");
context.setState(new OneState());
}
}
// state test
Context context = new Context();
context.handler();
context.handler();
context.handler();
context.handler();
6、观察者(Observer)模式
指多个对象之间存在一对多的关系,当一个对象状态发生改变时,会把这种改变通知给所有依赖它的对象,使它们改变自身行为。这种模式又被称为发布-订阅模式。
观察者模式的优缺点:
- 优点:
- 降低了目标与观察者之间的耦合,两者是抽象耦合。
- 两者之间有一套触发机制。
- 缺点:
- 当观察者有很多时,目标通知观察者将很耗时。
- 如果观察者与目标之间存在循环依赖,则会导致系统崩溃。
观察者模式的组件:
- 抽象目标接口:定义存储观察者的集合并提供维护观察者的方法,声明通知观察者的方法。
- 具体目标:实现抽象目标接口,实现其通知方法,。当自身状态发生改变时调用通知方法。
- 抽象观察者:声明接收的目标通知的方法(规范观察者的行为)。
- 具体观察者:实现抽象观察者并实现其接收通知的方法。
观察者模式的实现:
// 抽象目标
public abstract class Subject {
protected List<Observer> observers = new ArrayList<>();
protected void addObserver(Observer observer) {
observers.add(observer);
}
protected void removeObserver(Observer observer) {
observers.remove(observer);
}
abstract void notifying();
}
// 具体目标
public class Specific extends Subject {
@Override
void notifying() {
System.out.println("改变自身状态");
for (Observer observer : observers) {
observer.receive("specific");
}
}
}
// 抽象观察者
public interface Observer {
void receive(String notify);
}
// 具体观察者一
public class OneObserver implements Observer {
@Override
public void receive(String notify) {
System.out.println(notify);
}
}
// 具体观察者二
public class TwoObserver implements Observer {
@Override
public void receive(String notify) {
System.out.println(notify);
}
}
// observer test
Subject subject = new Specific();
Observer observer1 = new OneObserver();
Observer observer2 = new TwoObserver();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notifying();
7、中介者(Mediator)模式
定义一个中介者来封装原有多个对象之间的依赖关系,且可以独立改变它们之间的交互,以降低对象之间的耦合度。又被称为 “调停模式”,是迪米特法则的典型应用。
中介者模式的优缺点:
- 优点:
- 使关联类之间尽可能少的了解对方,符合迪米特法则。
- 将对象间一对多的关联关系转换为一对一,提高系统的灵活性,使系统更易于维护和扩展。
- 缺点:
- 当关联类增多时,中介者会变得复杂。
中介者模式的组件:
- 抽象中介者:声明关联关系注册与转发关联事件的方法。
- 具体中介者:实现抽象中介者,实现其注册和转发方法,且要维护关联者对象集合。
- 抽象关联者:引用中介者,并声明关联者的行为,即声明接收和发送请求的方法。
- 具体关联者:实现抽象关联者,实现其接收和发送请求的方法。
中介者模式的实现:
// 抽象中介者
public interface Mediator {
void registerRelation(Relation relation);
void relay(Relation relation);
}
// 具体中介者
public class OneMediator implements Mediator {
private final List<Relation> list = new ArrayList<>();
@Override
public void registerRelation(Relation relation) {
if (!list.contains(relation)) {
list.add(relation);
relation.setMediator(this);
}
}
@Override
public void relay(Relation relation) {
for (Relation relation1 : list) {
if (!relation.equals(relation1)) {
relation1.receive(relation);
}
}
}
}
// 具体关联者一
public class OneRelation extends Relation {
public OneRelation(String name) {
super(name);
}
@Override
void receive(Relation relation) {
System.out.println("OneRelation 接收到了 " + relation.getName() + " 的请求");
}
@Override
void send() {
System.out.println("OneRelation 发出请求");
mediator.relay(this);
}
}
// 具体关联者二
public class TwoRelation extends Relation {
public TwoRelation(String name) {
super(name);
}
@Override
void receive(Relation relation) {
System.out.println("TwoRelation 接收到了 " + relation.getName() + " 的请求");
}
@Override
void send() {
mediator.relay(this);
}
}
// mediator test
Mediator mediator = new OneMediator();
Relation relation1 = new OneRelation("OneRelation");
Relation relation2 = new TwoRelation("TwoRelation");
mediator.registerRelation(relation1);
mediator.registerRelation(relation2);
relation1.send();
8、迭代器(Iterator)模式
提供一种方法来顺序访问聚合对象内的元素,而不暴露聚合对象的内部数据结构。即将聚合对象的数据结构与遍历分开,或者说将聚合对象元素存储与遍历分开。
迭代器模式的优缺点:
- 优点:
- 迭代器的存在简化了聚合类。
- 支持以不同的方式遍历聚合对象,且在同一个聚合对象上可以遍历多次。
- 新增聚合类和其迭代器都很方便,无需修改原有代码。
- 缺点:
- 聚合类迭代器成对增加,在一定程度上增加了系统的复杂性。
迭代器模式的组件:
- 抽象迭代器:声明遍历聚合对象的方法,如 hasNext()、next() 等。
- 抽象聚合:声明操作元素的方法,如添加、移除等;声明获取其迭代器的方法。
- 具体聚合类:实现抽象聚合类,实现其操作元素和获取迭代器的方法。其中迭代器的实现可以内部类的形式实现,也可以以外部类的形式实现。
迭代器模式的实现:
// 抽象迭代器
public interface Iterator {
boolean hasNext();
Object next();
}
// 抽象聚合
public interface Container {
void add(Object o);
void remove(Object o);
Iterator getIterator();
}
// 具体聚合类 以内部类的形式实现迭代器
public class OneContainer implements Container {
protected List<Object> list = new ArrayList<>();
@Override
public void add(Object o) {
list.add(o);
}
@Override
public void remove(Object o) {
list.remove(o);
}
@Override
public Iterator getIterator() {
return new OneContainerIterator();
}
private class OneContainerIterator implements Iterator {
private int index = -1;
@Override
public boolean hasNext() {
return index < list.size() - 1;
}
@Override
public Object next() {
if (hasNext()) {
return list.get(++index);
}
return null;
}
}
}
// iterator test
Container container = new OneContainer();
container.add("one");
container.add("two");
container.add("three");
Iterator iterator = container.getIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
9、访问者(Visitor)模式
在不改变集合元素的前提下,为集合中的每个元素提供多种访问方式,即集合中的每个元素都有多个访问者对象。
访问者模式主要解决 稳定的数据结构和易变元素操作之间的耦合问题。
访问者模式的优缺点:
- 优点:
- 符合单一职责原则,即数据的存储和操作分别由元素类和访问者类实现。
- 优秀的扩展性和灵活性。
- 缺点:
- 具体元素对访问者公布了其细节,违反了迪米特法则。
- 具体元素的增加将导致需要在具体的访问者类中增加具体的操作,违反了开闭原则。
- 违反了依赖倒置原则,即依赖了具体类而不是依赖抽象。
访问者模式的组件:
- 抽象元素:声明接收访问者访问的方法。
- 具体元素:实现抽线元素类,实现其接收访问者的方法,以自身作为参数。
- 抽象访问者:声明对每个具体元素的访问方法。
- 具体访问者:实现抽象访问者类,实现其中的每个访问方法。
- 数据结构:即包含一组元素的容器,提供元素的增加、移除等方法,且需要提供访问者对象遍历访问容器元素的方法。
访问者模式的实现:
// 抽象元素
public interface Element {
void accept(Visitor visitor);
}
// 具体元素 a
public class ElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 具体元素 b
public class ElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 抽象访问者
public interface Visitor {
void visit(ElementA elementA);
void visit(ElementB elementB);
}
// 具体访问者 a
public class VisitorA implements Visitor {
@Override
public void visit(ElementA elementA) {
System.out.println("访问者 a 访问元素 a");
}
@Override
public void visit(ElementB elementB) {
System.out.println("访问者 a 访问元素 b");
}
}
// 具体访问者 b
public class VisitorB implements Visitor {
@Override
public void visit(ElementA elementA) {
System.out.println("访问者 b 访问元素 a");
}
@Override
public void visit(ElementB elementB) {
System.out.println("访问者 b 访问元素 b");
}
}
// 对象结构
public class ObjectStructure {
private List<Element> list = new ArrayList<>();
public void add(Element element) {
list.add(element);
}
public void remove(Element element) {
list.remove(element);
}
public void accept(Visitor visitor) {
Iterator<Element> iterator = list.iterator();
while (iterator.hasNext()) {
iterator.next().accept(visitor);
}
}
}
// visitor test
ObjectStructure objectStructure = new ObjectStructure();
Element elementa = new ElementA();
Element elementb = new ElementB();
objectStructure.add(elementa);
objectStructure.add(elementb);
Visitor visitora = new VisitorA();
Visitor visitorb = new VisitorB();
// 访问者 a 访问集合元素
objectStructure.accept(visitora);
// 访问者 b 访问集合元素
objectStructure.accept(visitorb);
10、备忘录(Memento)模式
在不破坏封装的前提下,获取并保存一个对象某一个时刻的内部状态,以便以后恢复它。“后悔药” 和游戏存档就是典型的备忘录实现。
备忘录模式的优缺点:
- 优点:
- 为用户提供了一种可以恢复某一时刻状态的机制,方便用户进行 “撤销” 操作。
- 缺点:
- 若保存在对象状态信息过多或保存状态过于频繁将会占用较大的内存资源。
备忘录模式的组件:
- 备忘录:负责保存对象的内部状态。
- 备忘录管理者:在内部维护一个备忘录类型的集合,并提供添加备忘录和获取某一个时刻的备忘录的方法。
- 备忘录拥有者:即备忘录的创建者/被保存的状态对应的那个对象,内部维护一个自身状态,并提供将状态保存到备忘录和从备忘录获取状态的方法。
备忘录模式的实现:
// 备忘录
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
// 备忘录管理者
public class CareTaker {
private final List<Memento> list = new ArrayList<>();
public void add(Memento memento) {
list.add(memento);
}
public Memento getMemento(int index) {
return list.get(index);
}
}
// 备忘录拥有者/那个对象
public class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Memento saveStateToMemento() {
return new Memento(state);
}
public void getStateFromMemento(Memento memento) {
this.state = memento.getState();
}
}
// memento test
CareTaker careTaker = new CareTaker();
Originator originator = new Originator();
// 设置状态一
originator.setState("state-one");
System.out.println("第一个状态 " + originator.getState());
// 将状态一保存至备忘录一并将备忘录一放入备忘录管理者
careTaker.add(originator.saveStateToMemento());
// 设置状态二
originator.setState("state-two");
System.out.println("第二个状态 " + originator.getState());
// 将状态二保存至备忘录二并将备忘录二放入备忘录管理者
careTaker.add(originator.saveStateToMemento());
// 设置状态三
originator.setState("state-three");
System.out.println("第三个状态 " + originator.getState());
// 将状态三保存至备忘录三并将备忘录放入备忘录管理者
careTaker.add(originator.saveStateToMemento());
// 获取状态一的备忘录以便恢复状态一
originator.getStateFromMemento(careTaker.getMemento(0));
System.out.println("第四个状态 " + originator.getState());
// 获取状态二的备忘录以便恢复状态二
originator.getStateFromMemento(careTaker.getMemento(1));
System.out.println("第五个状态 " + originator.getState());
11、解释器(Interpreter)模式
提供如何定义语言的文法,以及对语言句子的解释方法。
@XGLLHZ-一路生花.mp3