设计模式 | 简述 | 目的 | 生活案例 |
---|---|---|---|
工厂模式(Factory Pattern) | 不同条件下创建不同实例 | 封装创建细节 | 实体工厂 |
单例模式(Singleton Pattern) | 保证一个类仅有一个实例,并且提供一个全局访问点 | 保证独一无二 | CEO |
原型模式(Prototype Pattern) | 通过拷贝原型创建新的对象 | 高效创建对象 | 克隆 |
建造者模式(Builder Pattern) | 用来创建复杂的复合对象 | 开放个性配置步骤 | 选配 |
代理模式(Proxy Pattern) | 为其他对象提供一种代理以控制对这个对象的访问 | 增强职责 | 媒婆 |
外观模式(Facade Pattern) | 对外提供一个统一的接口用来访问子系统 | 统一访问入口 | 前台 |
装饰器模式(Decorator Pattern) | 为对象添加新功能 | 灵活扩展、同宗同源 | 煎饼 |
享元模式(Flyweight Pattern) | 使用对象池来减少重复对象的创建 | 共享资源池 | 全国社保联网 |
组合模式(Composite Pattern) | 将整体与局部(树形结构)进行递归组合,让客户端能够以一种的方式对其进行处理 | 统一整体和个体 | 组织架构树 |
适配器模式(Adapter Pattern) | 将原来不兼容的两个类融合在一起 | 兼容转换 | 电源适配 |
桥接模式(Bridge Pattern) | 将两个能够独立变化的部分分离开来 | 不允许用继承 | 桥 |
模板模式(Template Pattern) | 定义一套流程模板,根据需要实现模板中的操作 | 逻辑复用 | 把大象装进冰箱 |
策略模式(Strategy Pattern) | 封装不同的算法,算法之间能互相替换 | 把选择权交给用户 | 选择支付方式 |
责任链模式(Chain of Responsibility Pattern | ) 拦截的类都实现统一接口,每个接收者都包含对下一个接收者的引用。将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。 | 解耦处理逻辑 | 踢皮球 |
迭代器模式(Iterator Pattern) | 提供一种方法顺序访问一个聚合对象中的各个元素 | 统一对集合的访问方式 | 逐个检票进站 |
命令模式(Command Pattern) | 将请求封装成命令,并记录下来,能够撤销与重做 | 解耦请求和处理 | 遥控器 |
状态模式(State Pattern) | 根据不同的状态做出不同的行为 | 绑定状态和行为 | 订单状态跟踪 |
备忘录模式(Memento Pattern) | 保存对象的状态,在需要时进行恢复 | 备份、后悔机制 | 草稿箱 |
中介者模式(Mediator Pattern) | 将对象之间的通信关联关系封装到一个中介类中单独处理,从而使其耦合松散 | 统一管理网状资源 | 朋友圈 |
解释器模式(Interpreter Pattern) | 给定一个语言,定义它的语法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子 | 实现特定语法解析 | 摩斯密码 |
观察者模式(Observer Pattern) | 状态发生改变时通知观察者,一对多的关系 | 解耦观察者与被观察者 | 闹钟 |
访问者模式(Visitor Pattern) | 稳定数据结构,定义新的操作行为 | 解耦数据结构和数据操作 | KPI考核 |
委派模式(Delegate Pattern) | 允许对象组合实现与继承相同的代码重用,负责任务的调用和分配 | 只对结果负责 | 授权委托书 |
创建型模式
- 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
- 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
- 工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
- 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
- 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
单例模式(Singleton)
一个类只有一个实例,且该类能自行创建这个实例
单例模式的应用场景主要有以下几个方面。
- 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
- 某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
- 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
- 某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
- 频繁访问数据库或文件的对象。
- 对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。
- 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
痴汉模式
public class God {
private static final God god = new God();//God对象提前实例化
private God(){}//构造方法私有化
public static God getInstance(){//获得God对象方法公开化
return god;
}
}
懒汉模式
public class God {
private static God god;//God对象不进行实例化
private God(){}
public static God getInstance() {
if (god == null) {//不存在则创建
god = new God();
}
return god;
}
}
多线程懒汉模式
public class God {
private volatile static God god;
private God(){}
public static God getInstance() {
if (god == null) { //God不存在则进行排队,去创建god对象
synchronized(God.class){
if (god == null) {
god = new God();
}
}
}
//God产生后不必再排队
return god;
}
}
原型模式(Prototype)
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆。
原型模式通常适用于以下场景。
- 对象之间相同或相似,即只是个别的几个属性不同的时候。
- 创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
- 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
- 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Cloneable {
// 姓名
private String name;
// 年龄
private Integer age;
// 邮件
private String email;
// 描述
private String desc;
@Override
public Person clone() throws CloneNotSupportedException {
return (Person)super.clone();
}
}
----------------------------------------------
private static Person prototype = new Person("张三",20,"123456@qq.com","我是张三");
@SneakyThrows
public static void main(String[] args) {
List<Person> list = new ArrayList();
for (int i = 0; i < 10 ; i++) {
// 初始化一个对象
Person person = prototype.clone().setAge(i);
list.add(person);
}
System.out.println(list);
}
简单工厂模式
简单工厂模式的主要角色如下:
- 简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
- 抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
- 具体产品(ConcreteProduct):是简单工厂模式的创建目标
应用场景
- 对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。
抽象产品
public interface Product {
void show();
}
具体产品1
public class ProductA implements Product{
@Override
public void show() {
System.out.println("产品A");
}
}
具体产品2
public class ProductB implements Product{
@Override
public void show() {
System.out.println("产品B");
}
}
简单工厂类
public class SimpleFactory {
public static Product create(Class<? extends Product> clazz) {
if (Objects.nonNull(clazz)){
try {
return clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return null;
}
public static void main(String[] args) {
Product product = create(ProductA.class);
product.show();
}
}
工厂方法模式
应用场景:
- 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
- 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
- 客户不关心创建产品的细节,只关心产品的品牌
工厂方法模式的主要角色如下。
- 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
- 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
抽象产品
public interface Product {
void show();
}
具体产品1
public class ProductA implements Product{
@Override
public void show() {
System.out.println("产品A");
}
}
具体产品2
public class ProductB implements Product{
@Override
public void show() {
System.out.println("产品B");
}
}
抽象工厂
public interface AbstractFactory {
Product newProduct();
}
具体工厂1
public class FactoryA implements AbstractFactory{
@Override
public Product newProduct() {
System.out.println("具体工厂1生成-->具体产品1...");
return new ProductA();
}
}
具体工厂2
public class FactoryB implements AbstractFactory{
@Override
public Product newProduct() {
System.out.println("具体工厂2生成-->具体产品2...");
return new ProductB();
}
}
调用实际工厂生产产品
public static void main(String[] args) {
try {
Class<?> factoryA = Class.forName("com.example.tools_demo.factory.FactoryA");
AbstractFactory factory = (AbstractFactory)factoryA.newInstance();
factory.newProduct();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
抽象工厂模式
抽象工厂模式通常适用于以下场景:
- 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
- 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
- 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
抽象工厂模式的主要角色如下。
- 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
- 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
抽象工厂:提供了产品的生成方法
public interface AbstractFactory {
ProductA newProductA();
ProductB newProductB();
}
具体工厂:实现了产品的生成方法
public class FactoryA implements AbstractFactory{
@Override
public ProductA newProductA() {
System.out.println("具体工厂1生成-->具体产品1...");
return new ProductA();
}
@Override
public ProductB newProductB() {
System.out.println("具体工厂1生成-->具体产品2...");
return new ProductB();
}
}
调用实际工厂A 生产产品A和B
public static void main(String[] args) {
try {
Class<?> factoryA = Class.forName("com.example.tools_demo.factory.FactoryA");
AbstractFactory factory = (AbstractFactory)factoryA.newInstance();
factory.newProductA();
factory.newProductB();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
建造者模式
指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示
即产品的组成部分是不变的,但每一部分是可以灵活选择的。
建造者(Builder)模式的主要角色如下。
- 产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
- 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
- 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
- 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
建造者模式主要适用于以下应用场景:
- 相同的方法,不同的执行顺序,产生不同的结果。
- 多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。
- 产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。
- 初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。
产品角色:包含多个组成部件的复杂对象
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 void show() {
System.out.println("partA: " + partA);
System.out.println("partB: " + partB);
System.out.println("partC: " + partC);
}
}
抽象建造者:包含创建产品各个子部件的抽象方法
public abstract class Builder {
//创建产品对象
protected Product product = new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
//返回产品对象
public Product getResult() {
return product;
}
}
具体建造者1:实现了抽象建造者接口
public class ConcreteBuilderA extends Builder {
public void buildPartA() {
product.setPartA("A 建造 PartA");
}
public void buildPartB() {
product.setPartB("A 建造 PartB");
}
public void buildPartC() {
product.setPartC("A 建造 PartC");
}
}
具体建造者2:实现了抽象建造者接口
public class ConcreteBuilderB extends Builder {
public void buildPartA() {
product.setPartA("B 建造 PartA");
}
public void buildPartB() {
product.setPartB("B 建造 PartB");
}
public void buildPartC() {
product.setPartC("B 建造 PartC");
}
}
指挥者:调用建造者中的方法完成复杂对象的创建
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
//产品构建与组装方法
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
// 只关心builder类,并不关心谁生成的bulder类的具体实现
// 建造顺序固定
public static void main(String[] args) {
Builder builder = new ConcreteBuilderA();
Director director = new Director(builder);
Product product = director.construct();
product.show();
}
}
结构型模式
- 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
- 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
- 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现的,从而降低了抽象和实现这两个可变维度的耦合度。
- 装饰(Decorator)模式:动态地给对象增加一些职责,即增加其额外的功能。
- 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
- 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
- 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
代理模式
代理模式的主要角色如下。
- 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
代理模式的应用场景:
- 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
- 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
- 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
- 智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。
- 延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载。
静态:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
动态:在程序运行时,运用反射机制动态创建而成
静态代理
抽象主题
public interface Subject {
void request();
}
真实主题
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("访问真实主题方法...");
}
}
代理
public class MyProxy implements Subject {
private RealSubject realSubject;
@Override
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
preRequest();
realSubject.request();
postRequest();
}
public void preRequest() {
System.out.println("访问真实主题之前的预处理。");
}
public void postRequest() {
System.out.println("访问真实主题之后的后续处理。");
}
}
对真实主题的方法进行额外处理
public static void main(String[] args) {
MyProxy proxy = new MyProxy();
proxy.request();
}
动态代理
JDK 实现代理只需要使用 newProxyInstance 方法,该方法需要接收三个参数
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h )
- ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的
- Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型
- InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,把当前执行目标对象的方法作为参数传入
实现动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxy implements InvocationHandler {
private Subject target;
public Subject getInstance(Subject target) {
this.target = target;
Class<?> clazz = target.getClass();
/**
*三个参数,
*第一个是被代理类的类构造器,
*第二个指的是被代理类的接口,也就是Subject接口,
*第三个是实现这个代理的类
*
**/
return (Subject) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
preRequest();
Object result = method.invoke(this.target, args);
postRequest();
return result;
}
public void preRequest() {
System.out.println("访问真实主题之前的预处理。");
}
public void postRequest() {
System.out.println("访问真实主题之后的后续处理。");
}
}
代理对象
public class MyProxy implements Subject {
private RealSubject realSubject;
@Override
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
realSubject.request();
}
}
对真实主题方法进行额外处理
MyProxy proxy = new MyProxy();
JdkProxy jdkProxy = new JdkProxy();
Subject subject = jdkProxy.getInstance(proxy);
subject.request();
适配器模式
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作
适配器模式(Adapter)通常适用于以下场景。
- 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
- 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。
适配器模式(Adapter)包含以下主要角色。
- 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
- 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
- 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
例如:一个烧水壶是使用两个插孔的插座。实现两孔插座的方法。但是现在只有三孔插座
三孔插座类
/**
* 三孔插座
*/
public interface TriplexOutlet {
void electrify(int l, int n, int e);
}
两孔插座类
/**
* 两孔插座
*/
public interface DualOutlet {
void electrify(int l, int n);
}
两孔插座电器
/**
* 烧水壶类
*/
public class Teakettle implements DualOutlet {
@Override
public void electrify(int l, int n) {
System.out.println("实现两孔插座");
System.out.println("火线:" + l + " 零线:" + n);
}
}
类适配器模式
通过继承来实现适配器功能。
适配器类
/**
* 适配器类
*/
public class Adapter extends Teakettle implements TriplexOutlet {
@Override
public void triplexElectrify(int l, int n, int e) {
System.out.println("三孔插座使用两孔插座逻辑");
dualElectrify(l, n);
}
}
使用插孔
/**
* 使用插孔
*/
public class Boil {
public static void main(String[] args) {
Adapter adapter = new Adapter();
adapter.triplexElectrify(1, 2, 3);
}
}
对象适配器模式
通过对象注入来实现适配器功能。
适配器类
/**
* 适配器类
*/
public class Adapter implements TriplexOutlet {
private Teakettle teakettle;
public Adapter(Teakettle teakettle) {
this.teakettle = teakettle;
}
@Override
public void triplexElectrify(int l, int n, int e) {
System.out.println("三孔插座使用两孔插座逻辑");
teakettle.dualElectrify(l, n);
}
}
使用插孔
/**
* 使用插孔
*/
public class Boil {
public static void main(String[] args) {
Adapter adapter = new Adapter(new Teakettle());
adapter.triplexElectrify(1, 2, 3);
}
}
接口适配器模式
通过抽象类来实现适配
适配器类
/**
* 适配器类
*/
public abstract class Adapter implements Appliance {
@Override
public void triplexElectrify(int l, int n, int e) {
}
@Override
public void dualElectrify(int l, int n) {
}
}
使用插孔
/**
* 使用插孔
*/
public class Boil extends Adapter{
@Override
public void dualElectrify(int l, int n) {
System.out.println("只实现用自己要的方法");
}
}
桥接模式
将抽象与实现分离,使它们可以独立变化;
桥接(Bridge)模式包含以下主要角色。
- 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
- 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
- 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
- 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
桥接模式通常适用于以下场景。
- 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
- 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
- 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。
抽象角色
//抽象角色
public abstract class Abstraction {
protected Implementor imple;
protected Abstraction(Implementor imple) {
this.imple = imple;
}
public abstract void Operation();
}
抽象角色扩展
//扩展抽象角色
public class RefinedAbstraction extends Abstraction {
protected RefinedAbstraction(Implementor imple) {
super(imple);
}
public void Operation() {
System.out.println("扩展抽象化(Refined Abstraction)角色被访问");
imple.OperationImpl();
}
}
实现角色
//实现者角色
public interface Implementor {
void OperationImpl();
}
实现角色扩展
//具体实现者角色
public class ConcreteImplementorA implements Implementor {
public void OperationImpl() {
System.out.println("具体实现化(Concrete Implementor)角色被访问");
}
}
角色和实现各自对自己进行扩展,调用时不关心各自的扩展内容
public class BridgeTest {
public static void main(String[] args) {
Implementor imple = new ConcreteImplementorA();
Abstraction abs = new RefinedAbstraction(imple);
abs.Operation();
}
}
装饰器模式
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式
装饰器模式主要包含以下角色。
- 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
- 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
- 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
- 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
装饰器模式的应用场景:
- 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
- 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现。
- 当对象的功能要求可以动态地添加,也可以再动态地撤销时。
抽象构建
//抽象构件角色
public interface Component {
void operation();
}
具体构建
//具体构件角色
public class ConcreteComponent implements Component {
public ConcreteComponent() {
System.out.println("创建具体构件角色");
}
public void operation() {
System.out.println("调用具体构件角色的方法operation()");
}
}
抽象装饰
//抽象装饰角色
public abstract class Decorator implements Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
具体装饰1
//具体装饰角色1
public class ConcreteDecorator1 extends Decorator {
public ConcreteDecorator1(Component component) {
super(component);
}
public void operation() {
super.operation();
addedFunction();
}
public void addedFunction() {
System.out.println("为具体构件角色增加额外的功能 1");
}
}
具体装饰2
//具体装饰角色
public class ConcreteDecorator2 extends Decorator {
public ConcreteDecorator2(Component component) {
super(component);
}
public void operation() {
super.operation();
addedFunction();
}
public void addedFunction() {
System.out.println("为具体构件角色增加额外的功能 2");
}
}
角色构建很复杂时,构建器构建角色抽取出来,单独构建。装饰器对构建后的进行加工装饰
public class DecoratorTest {
public static void main(String[] args) {
Component a = new ConcreteComponent();
//构建角色
a.operation();
System.out.println("---------------------------------");
Component b = new ConcreteDecorator1(a);
//对已构建角色装饰装饰
b.operation();
System.out.println("---------------------------------");
Component c = new ConcreteDecorator2(b);
//对已装饰角色装饰装饰
c.operation();
}
}
外观模式
通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。
外观(Facade)模式包含以下主要角色。
- 外观(Facade)角色:为多个子系统对外提供一个共同的接口。
- 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
- 客户(Client)角色:通过一个外观角色访问各个子系统的功能。
外观模式的应用场景
- 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
- 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
- 当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。
外观角色
//外观角色
public class Facade {
private SubSystem01 obj1 = new SubSystem01();
private SubSystem02 obj2 = new SubSystem02();
private SubSystem03 obj3 = new SubSystem03();
public void method() {
obj1.method1();
obj2.method2();
obj3.method3();
}
}
子系统1
//子系统角色
public class SubSystem01 {
public void method1() {
System.out.println("子系统01的method1()被调用!");
}
}
子系统2
//子系统角色
public class SubSystem02 {
public void method2() {
System.out.println("子系统02的method2()被调用!");
}
}
子系统3
//子系统角色
public class SubSystem03 {
public void method3() {
System.out.println("子系统03的method3()被调用!");
}
}
用户调用外观角色,外观角色去调用每个子系统
public class FacadeTest {
public static void main(String[] args) {
Facade f = new Facade();
f.method();
}
}
享元模式
运用共享技术来有效地支持大量细粒度对象的复用
享元模式的主要角色有如下。
- 抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
- 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
- 非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
- 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
享元模式的应用场景:
- 系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源。
- 大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。
- 由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式。
非享元对象
//非享元角色
public class UnsharedConcreteFlyweight {
private String info;
UnsharedConcreteFlyweight(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
抽象享元角色
//抽象享元角色
public interface Flyweight {
void operation(UnsharedConcreteFlyweight state);
}
具体享元角色
//具体享元角色
public class ConcreteFlyweight implements Flyweight {
private String key;
ConcreteFlyweight(String key) {
this.key = key;
System.out.println("具体享元" + key + "被创建!");
}
@Override
public void operation(UnsharedConcreteFlyweight outState) {
System.out.print("具体享元" + key + "被调用,");
System.out.println("非享元信息是:" + outState.getInfo());
}
}
享元工厂角色
//享元工厂角色
public class FlyweightFactory {
private HashMap<String, Flyweight> flyweights = new HashMap<String, Flyweight>();
public Flyweight getFlyweight(String key) {
Flyweight flyweight = (Flyweight) flyweights.get(key);
if (flyweight != null) {
System.out.println("具体享元" + key + "已经存在,被成功获取!");
} else {
flyweight = new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
}
return flyweight;
}
}
将资源放进资源池中,从资源池调用资源
public class FlyweightTest {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight f01 = factory.getFlyweight("a");
Flyweight f02 = factory.getFlyweight("a");
Flyweight f03 = factory.getFlyweight("a");
Flyweight f11 = factory.getFlyweight("b");
Flyweight f12 = factory.getFlyweight("b");
f01.operation(new UnsharedConcreteFlyweight("第1次调用a。"));
f02.operation(new UnsharedConcreteFlyweight("第2次调用a。"));
f03.operation(new UnsharedConcreteFlyweight("第3次调用a。"));
f11.operation(new UnsharedConcreteFlyweight("第1次调用b。"));
f12.operation(new UnsharedConcreteFlyweight("第2次调用b。"));
}
}
组合模式
将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系
组合模式包含以下主要角色。
- 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)
- 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
- 树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
组合模式的应用场景
- 在需要表示一个对象整体与部分的层次结构的场合。
- 要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。
抽象组件
public abstract class Node {
protected String name;//节点命名
public Node(String name) {//构造节点,传入节点名。
this.name = name;
}
//增加后续子节点方法
protected abstract void add(Node child);
protected void ls(int space) {
for (int i = 0; i < space; i++) {
System.out.print(" ");//先循环输出n个空格;
}
System.out.println(name);//然后再打印自己的名字。
}
//无参重载方法,默认从第0列开始显示。
protected void ls() {
this.ls(0);
}
}
树枝节点
public class Folder extends Node {
//文件夹可以包含子节点(文件夹或者文件)。
private List<Node> childrenNodes = new ArrayList<>();
public Folder(String name) {
super(name);//调用父类“节点”的构造方法命名。
}
@Override
protected void add(Node child) {
childrenNodes.add(child);//可以添加子节点。
}
@Override
public void ls(int space) {
super.ls(space);//调用父类共通的ls方法列出自己的名字。
space++;//之后列出的子节点前,空格数要增加一个了。
for (Node node : childrenNodes) {
node.ls(space);//调用子节点的ls方法。
}
}
}
树叶节点
public class File extends Node {
public File(String name) {
super(name);
}
@Override
protected void add(Node child) {
System.out.println("不能添加子节点。");
}
@Override
public void ls(int space) {
super.ls(space);
}
}
调用
public class CompositeTest {
public static void main(String[] args) {
Node driveD = new Folder("D盘");
Node doc = new Folder("文档");
doc.add(new File("简历.doc"));
doc.add(new File("项目介绍.ppt"));
driveD.add(doc);
Node music = new Folder("音乐");
Node jay = new Folder("周杰伦");
jay.add(new File("双截棍.mp3"));
jay.add(new File("告白气球.mp3"));
jay.add(new File("听妈妈的话.mp3"));
Node jack = new Folder("张学友");
jack.add(new File("吻别.mp3"));
jack.add(new File("一千个伤心的理由.mp3"));
music.add(jay);
music.add(jack);
driveD.add(music);
driveD.ls();
}
}
行为型模式
- 模板方法(Template Method)模式:定义一个操作中的算法骨架,将算法的一些步骤延迟到子类中,使得子类在可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
- 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
- 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。 - 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
- 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
- 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
- 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
- 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
- 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
- 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。
模板方法模式
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
模板方法模式包含以下主要角色。
- 抽象类/抽象模板(Abstract Class)
抽象模板类,负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。这些方法的定义如下。- 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
- 基本方法:是整个算法中的一个步骤,包含以下几种类型。
- 抽象方法:在抽象类中声明,由具体子类实现。
- 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
- 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
- 具体子类/具体实现(Concrete Class)
- 具体实现类,实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。
模式的应用场景
- 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
- 当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
- 当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。
钩子方法的抽象类
//含钩子方法的抽象类
public abstract class HookAbstractClass {
//模板方法
public void templateMethod() {
abstractMethod1();
hookMethod1();
if (hookMethod2()) {
specificMethod();
}
abstractMethod2();
}
//具体方法
public void specificMethod() {
System.out.println("抽象类中的具体方法被调用...");
}
//钩子方法1
public void hookMethod1() {
}
//钩子方法2
public boolean hookMethod2() {
return true;
}
//抽象方法1
public abstract void abstractMethod1();
//抽象方法2
public abstract void abstractMethod2();
}
含钩子方法的具体子类
//含钩子方法的具体子类
public class HookConcreteClass extends HookAbstractClass {
public void abstractMethod1() {
System.out.println("抽象方法1的实现被调用...");
}
public void abstractMethod2() {
System.out.println("抽象方法2的实现被调用...");
}
public void hookMethod1() {
System.out.println("钩子方法1被重写...");
}
public boolean hookMethod2() {
return false;
}
}
具体子类创建抽象方法,重写钩子函数,调用模板方法时会调用子类重写后的方法
public class HookTemplateTest {
public static void main(String[] args) {
HookAbstractClass tm = new HookConcreteClass();
tm.templateMethod();
}
}
策略模式
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。
策略模式的主要角色如下。
- 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用
策略模式的应用场景:
- 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
- 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
- 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
- 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。
抽象策略类
//抽象策略类
interface Strategy {
public void strategyMethod(); //策略方法
}
策略类A
//具体策略类A
class ConcreteStrategyA implements Strategy {
public void strategyMethod() {
System.out.println("具体策略A的策略方法被访问!");
}
}
策略类B
//具体策略类B
class ConcreteStrategyB implements Strategy {
public void strategyMethod() {
System.out.println("具体策略B的策略方法被访问!");
}
}
环境类
//环境类
class Context {
private Strategy strategy;
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void strategyMethod() {
strategy.strategyMethod();
}
}
使用
public class StrategyPattern {
public static void main(String[] args) {
Context c = new Context();
Strategy s = new ConcreteStrategyA();
c.setStrategy(s);
c.strategyMethod();
System.out.println("-----------------");
s = new ConcreteStrategyB();
c.setStrategy(s);
c.strategyMethod();
}
}
命令模式
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开;
命令模式包含以下主要角色。
- 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
- 具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
- 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
- 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
命令模式通常适用于以下场景。
- 请求调用者需要与请求接收者解耦时,命令模式可以使调用者和接收者不直接交互。
- 系统随机请求命令或经常增加、删除命令时,命令模式可以方便地实现这些功能。
- 当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。
- 当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现。
抽象命令类
//抽象命令
public interface AbstractCommand {
void execute();
}
调用者
//树枝构件: 调用者
public class CompositeInvoker implements AbstractCommand {
private ArrayList<AbstractCommand> children = new ArrayList<AbstractCommand>();
public void add(AbstractCommand c) {
children.add(c);
}
public void remove(AbstractCommand c) {
children.remove(c);
}
public AbstractCommand getChild(int i) {
return children.get(i);
}
public void execute() {
for (Object obj : children) {
((AbstractCommand) obj).execute();
}
}
}
接收者
//接收者
public class CompositeReceiver {
public void action1() {
System.out.println("接收者的action1()方法被调用...");
}
public void action2() {
System.out.println("接收者的action2()方法被调用...");
}
}
具体命令1
//树叶构件: 具体命令1
public class ConcreteCommand1 implements AbstractCommand {
private CompositeReceiver receiver;
ConcreteCommand1() {
receiver = new CompositeReceiver();
}
public void execute() {
receiver.action1();
}
}
具体命令2
//树叶构件: 具体命令2
public class ConcreteCommand2 implements AbstractCommand {
private CompositeReceiver receiver;
ConcreteCommand2() {
receiver = new CompositeReceiver();
}
public void execute() {
receiver.action2();
}
}
将两个命令分别调用接收者不同方法,封装到调用者中,调用者执行调用方法,循环调用每个命令
public class CompositeCommandTest {
public static void main(String[] args) {
AbstractCommand cmd1 = new ConcreteCommand1();
AbstractCommand cmd2 = new ConcreteCommand2();
CompositeInvoker ir = new CompositeInvoker();
ir.add(cmd1);
ir.add(cmd2);
System.out.println("客户访问调用者的execute()方法...");
ir.execute();
}
}
责任链模式
为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
职责链模式主要包含以下角色。
- 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
- 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
- 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
模式的应用场景
- 多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
- 可动态指定一组对象处理请求,或添加新的处理者。
- 需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
** 抽象处理者**
public abstract class Approver {// 审批人抽象类
protected String name;// 抽象出审批人的姓名。
protected Approver nextApprover;// 下一个审批人,更高级别领导。
public Approver(String name) {
this.name = name;
}
protected Approver setNextApprover(Approver nextApprover) {
this.nextApprover = nextApprover;
return this.nextApprover;// 返回下个审批人,链式编程。
}
public abstract void approve(int amount);// 抽象审批方法由具体审批人子类实现
}
具体处理者:员工
public class Staff extends Approver {
public Staff(String name) {
super(name);
}
@Override
public void approve(int amount) {
if (amount <= 1000) {
System.out.println("审批通过。【员工:" + name + "】");
} else {
System.out.println("无权审批,升级处理。【员工:" + name + "】");
this.nextApprover.approve(amount);
}
}
}
具体处理者:经理
public class Manager extends Approver {
public Manager(String name) {
super(name);
}
@Override
public void approve(int amount) {
if (amount <= 5000) {
System.out.println("审批通过。【经理:" + name + "】");
} else {
System.out.println("无权审批,升级处理。【经理:" + name + "】");
this.nextApprover.approve(amount);
}
}
}
具体处理者:CEO
public class CEO extends Approver {
public CEO(String name) {
super(name);
}
@Override
public void approve(int amount) {
if (amount <= 10000) {
System.out.println("审批通过。【CEO:" + name + "】");
} else {
System.out.println("驳回申请。【CEO:" + name + "】");
}
}
}
客户角色
审批自动进行,直到结束
public class Client {
public static void main(String[] args) {
Approver flightJohn = new Staff("张飞");
flightJohn.setNextApprover(new Manager("关羽")).setNextApprover(new CEO("刘备"));
//高层接触不到也没必要接触,直接找员工张飞审批。
flightJohn.approve(1000);
/**
审批通过。【员工:张飞】
**/
flightJohn.approve(4000);
/**
无权审批,升级处理。【员工:张飞】
审批通过。【经理:关羽】
**/
flightJohn.approve(9000);
/**
无权审批,升级处理。【员工:张飞】
无权审批,升级处理。【经理:关羽】
审批通过。【CEO:刘备】
**/
flightJohn.approve(88000);
/**
无权审批,升级处理。【员工:张飞】
无权审批,升级处理。【经理:关羽】
驳回申请。【CEO:刘备】
**/
}
}
状态模式
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
状态模式包含以下主要角色。
- 环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
- 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
- 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
通常在以下情况下可以考虑使用状态模式。
- 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
- 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
抽象状态
//抽象状态类
public abstract class State {
public abstract void handle(Context context);
}
具体状态A类
//具体状态A类
public class ConcreteStateA extends State {
public void handle(Context context) {
System.out.println("当前状态是 A.");
context.setState(new ConcreteStateB());
}
}
具体状态B类
//具体状态B类
public class ConcreteStateB extends State {
public void handle(Context context) {
System.out.println("当前状态是 B.");
context.setState(new ConcreteStateA());
}
}
环境类
//环境类
public class Context {
private State state;
//定义环境类的初始状态
public Context() {
this.state = new ConcreteStateA();
}
//设置新状态
public void setState(State state) {
this.state = state;
}
//读取状态
public State getState() {
return (state);
}
//对请求做处理
public void Handle() {
state.handle(this);
}
}
默认状态为A,多次调用自动转换状态
public class StatePatternClient {
public static void main(String[] args) {
Context context = new Context(); //创建环境
context.Handle(); //处理请求
context.Handle();
context.Handle();
context.Handle();
}
}
观察者模式
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式
观察者模式的主要角色如下。
- 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
- 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
- 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
- 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
模式的应用场景
- 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 实现类似广播机制的功能,不需要知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播。
- 多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知。
抽象主题
//抽象目标
public abstract class Subject {
protected List<Observer> observers = new ArrayList<Observer>();
//增加观察者方法
public void add(Observer observer) {
observers.add(observer);
}
//删除观察者方法
public void remove(Observer observer) {
observers.remove(observer);
}
public abstract void notifyObserver(); //通知观察者方法
}
具体主题
//具体目标
public class ConcreteSubject extends Subject {
public void notifyObserver() {
System.out.println("具体目标发生改变...");
System.out.println("--------------");
for (Observer obs : observers) {
obs.response();
}
}
}
抽象观察者
//抽象观察者
public interface Observer {
void response(); //反应
}
具体观察者1
//具体观察者1
public class ConcreteObserver1 implements Observer {
public void response() {
System.out.println("具体观察者1作出反应!");
}
}
具体观察者2
//具体观察者2
public class ConcreteObserver2 implements Observer {
public void response() {
System.out.println("具体观察者2作出反应!");
}
}
主题发生改变,循环通知观察者
public class ObserverPattern {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
Observer obs1 = new ConcreteObserver1();
Observer obs2 = new ConcreteObserver2();
subject.add(obs1);
subject.add(obs2);
subject.notifyObserver();
}
}
中介者模式
定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互
中介者模式包含以下主要角色。
- 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
- 具体中介者(Concrete Mediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
- 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
- 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。
模式的应用场景
- 当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。
- 当想创建一个运行于多个类之间的对象,又不想生成新的子类时。
抽象中介者
//抽象中介者
public abstract class Mediator {
public abstract void register(Colleague colleague);
public abstract void relay(Colleague cl); //转发
}
具体中介者
//具体中介者
public class ConcreteMediator extends Mediator {
private List<Colleague> colleagues = new ArrayList<Colleague>();
public void register(Colleague colleague) {
if (!colleagues.contains(colleague)) {
colleagues.add(colleague);
colleague.setMedium(this);
}
}
public void relay(Colleague cl) {
for (Colleague ob : colleagues) {
if (!ob.equals(cl)) {
((Colleague) ob).receive();
}
}
}
}
抽象同事类
//抽象同事类
public abstract class Colleague {
protected Mediator mediator;
public void setMedium(Mediator mediator) {
this.mediator = mediator;
}
public abstract void receive();
public abstract void send();
}
具体同事类1
//具体同事类
public class ConcreteColleague1 extends Colleague {
public void receive() {
System.out.println("具体同事类1收到请求。");
}
public void send() {
System.out.println("具体同事类1发出请求。");
mediator.relay(this); //请中介者转发
}
}
具体同事类2
//具体同事类
public class ConcreteColleague2 extends Colleague {
public void receive() {
System.out.println("具体同事类2收到请求。");
}
public void send() {
System.out.println("具体同事类2发出请求。");
mediator.relay(this); //请中介者转发
}
}
同事类发送信息到中介类,中介类将信息发送到除发送者的其他同事类
public class MediatorPattern {
public static void main(String[] args) {
Mediator md = new ConcreteMediator();
Colleague c1, c2;
c1 = new ConcreteColleague1();
c2 = new ConcreteColleague2();
md.register(c1);
md.register(c2);
c1.send();
System.out.println("-------------");
c2.send();
}
}
迭代器模式
提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示;
迭代器模式主要包含以下角色。
- 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
- 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
- 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
- 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
模式的应用场景
- 当需要为聚合对象提供多种遍历方式时。
- 当需要为遍历不同的聚合结构提供一个统一的接口时。
- 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。
抽象聚合
//抽象聚合
public interface Aggregate {
void add(Object obj);
void remove(Object obj);
Iterator getIterator();
}
具体聚合
//具体聚合
public class ConcreteAggregate implements Aggregate {
private List<Object> list = new ArrayList<Object>();
public void add(Object obj) {
list.add(obj);
}
public void remove(Object obj) {
list.remove(obj);
}
public Iterator getIterator() {
return (new ConcreteIterator(list));
}
}
抽象迭代器
//抽象迭代器
public interface Iterator {
Object first();
Object next();
boolean hasNext();
}
具体迭代器
//具体迭代器
public class ConcreteIterator implements Iterator {
private List<Object> list = null;
private int index = -1;
public ConcreteIterator(List<Object> list) {
this.list = list;
}
public boolean hasNext() {
if (index < list.size() - 1) {
return true;
} else {
return false;
}
}
public Object first() {
index = 0;
Object obj = list.get(index);
;
return obj;
}
public Object next() {
Object obj = null;
if (this.hasNext()) {
obj = list.get(++index);
}
return obj;
}
}
调用
public class IteratorPattern {
public static void main(String[] args) {
Aggregate ag = new ConcreteAggregate();
ag.add("JAVA");
ag.add("PHP");
ag.add("C++");
System.out.print("聚合的内容有:");
Iterator it = ag.getIterator();
while (it.hasNext()) {
Object ob = it.next();
System.out.print(ob.toString() + "\t");
}
Object ob = it.first();
System.out.println("\nFirst:" + ob.toString());
}
}
访问者模式
将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式
访问者模式包含以下主要角色。
- 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
- 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
- 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
- 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
- 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
模式的应用场景
- 对象结构相对稳定,但其操作算法经常变化的程序。
- 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
- 对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。
抽象访问者
//抽象访问者
public interface Visitor {
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
具体访问者A类
//具体访问者A类
public class ConcreteVisitorA implements Visitor {
public void visit(ConcreteElementA element) {
System.out.println("具体访问者A访问-->" + element.operationA());
}
public void visit(ConcreteElementB element) {
System.out.println("具体访问者A访问-->" + element.operationB());
}
}
具体访问者B类
//具体访问者B类
public class ConcreteVisitorB implements Visitor {
public void visit(ConcreteElementA element) {
System.out.println("具体访问者B访问-->" + element.operationA());
}
public void visit(ConcreteElementB element) {
System.out.println("具体访问者B访问-->" + element.operationB());
}
}
抽象元素
//抽象元素类
public interface Element {
void accept(Visitor visitor);
}
具体元素A类
//具体元素A类
public class ConcreteElementA implements Element {
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationA() {
return "具体元素A的操作。";
}
}
具体元素B类
//具体元素B类
public class ConcreteElementB implements Element {
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationB() {
return "具体元素B的操作。";
}
}
对象结构角色
//对象结构角色
public class ObjectStructure {
private List<Element> list = new ArrayList<Element>();
public void accept(Visitor visitor) {
Iterator<Element> i = list.iterator();
while (i.hasNext()) {
i.next().accept(visitor);
}
}
public void add(Element element) {
list.add(element);
}
public void remove(Element element) {
list.remove(element);
}
}
对集合中的不同类型数据(类型数量稳定)进行多种操作时,使用访问者模式
public class VisitorPattern {
public static void main(String[] args) {
ObjectStructure os = new ObjectStructure();
os.add(new ConcreteElementA());
os.add(new ConcreteElementB());
Visitor visitor = new ConcreteVisitorA();
os.accept(visitor);
System.out.println("------------------------");
visitor = new ConcreteVisitorB();
os.accept(visitor);
}
}
备忘录模式
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态
备忘录模式的主要角色如下。
- 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
- 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
- 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
模式的应用场景
- 需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。
- 需要提供一个可回滚操作的场景,如 Word、记事本、Photoshop,Eclipse 等软件在编辑时按 Ctrl+Z 组合键,还有数据库中事务操作。
发起人
//发起人
public class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento createMemento() {
return new Memento(state);
}
public void restoreMemento(Memento m) {
this.setState(m.getState());
}
}
管理者
//管理者
public class Caretaker {
private Memento memento;
public void setMemento(Memento m) {
memento = m;
}
public Memento getMemento() {
return memento;
}
}
备忘录
//备忘录
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
发起人创建备忘录,将当前对象存储到备忘录中,管理者保存备忘录。
发起人恢复时,从管理者中拿到备忘录,回复数据
public class MementoPattern {
public static void main(String[] args) {
Originator or = new Originator();
Caretaker cr = new Caretaker();
or.setState("S0");
System.out.println("初始状态:" + or.getState());
cr.setMemento(or.createMemento()); //保存状态
or.setState("S1");
System.out.println("新的状态:" + or.getState());
or.restoreMemento(cr.getMemento()); //恢复状态
System.out.println("恢复状态:" + or.getState());
}
}
解释器模式
给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子
解释器模式包含以下主要角色。
- 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
- 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
- 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
- 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
- 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
模式的应用场景
- 当语言的文法较为简单,且执行效率不是关键问题时。
- 当问题重复出现,且可以用一种简单的语言来进行表达时。
- 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。
抽象表达式类
//抽象表达式类
public interface Expression {
boolean interpret(String info);
}
非终结符表达式类
//非终结符表达式类
class AndExpression implements Expression {
private Expression city = null;
private Expression person = null;
public AndExpression(Expression city, Expression person) {
this.city = city;
this.person = person;
}
public boolean interpret(String info) {
String s[] = info.split("的");
return city.interpret(s[0]) && person.interpret(s[1]);
}
}
终结符表达式类
//终结符表达式类
public class TerminalExpression implements Expression {
private Set<String> set = new HashSet<String>();
public TerminalExpression(String[] data) {
for (int i = 0; i < data.length; i++) set.add(data[i]);
}
public boolean interpret(String info) {
if (set.contains(info)) {
return true;
}
return false;
}
}
环境类
//环境类
class Context {
private String[] citys = {"韶关", "广州"};
private String[] persons = {"老人", "妇女", "儿童"};
private Expression cityPerson;
public Context() {
Expression city = new TerminalExpression(citys);
Expression person = new TerminalExpression(persons);
cityPerson = new AndExpression(city, person);
}
public void freeRide(String info) {
boolean ok = cityPerson.interpret(info);
if (ok) System.out.println("您是" + info + ",您本次乘车免费!");
else System.out.println(info + ",您不是免费人员,本次乘车扣费2元!");
}
}
用【的】去拆分语句,然后去判断是否在表达式中。
/*文法规则
<expression> ::= <city>的<person>
<city> ::= 韶关|广州
<person> ::= 老人|妇女|儿童
*/
public class InterpreterPatternDemo {
public static void main(String[] args) {
Context bus = new Context();
bus.freeRide("韶关的老人");
bus.freeRide("韶关的年轻人");
bus.freeRide("广州的妇女");
bus.freeRide("广州的儿童");
bus.freeRide("山东的儿童");
}
}