深入理解常见的二十三种设计模式

文章目录

  • 深入理解常见的二十三种设计模式
    • 一、设计模式的分类
      • 1.1 创建型(五种)
      • 1.2 结构型(七种)
      • 1.3 行为型(十一种)
    • 二、创建型
      • 2.1 单例模式
      • 2.2 工厂方法模式
      • 2.3 抽象工厂模式
      • 2.4 构造器模式
      • 2.5 原型模式
    • 三、结构型
      • 3.1 适配器模式(adapter)
      • 3.2 装饰者模式
      • 3.3 代理模式
      • 3.4 桥接模式
      • 3.5 享元模式
      • 3.6 外观模式
      • 3.7 组合模式
        • (1)示例一:文件和目录
        • (2)示例二:组合模式和迭代器模式一块使用
    • 三、行为型
      • 3.1 职责链模式
        • (1)职责链的第一种实现方式:用链表存放处理器
        • (2)职责链的第二种实现方式:用数组存放处理器
        • (3)职责链的一种变种:请求会被所有的处理器都处理一遍,不存在中途终止的情况
        • (4)应用场景:论谈发帖审核
        • (5)职责链在开发框架中的应用:过滤器(Servlet Filter)
        • (6)职责链在开发框架中的应用:拦截器(Spring Interceptor)
        • (7)AOP(面向切面)、Servlet Filter(过滤器)、Spring Intercepter(拦截器)都可以做访问控制功能,区别是什么?
      • 3.2 解释器模式
        • (1)示例一:根据计算规则,获取输入内容的结果
          • 版本一:不使用解释器模式
          • 版本二:使用解释器模式进行重构
        • (2)示例二:监视器中的自定义警告规则
      • 3.3 命令模式
        • 示例一:遥控板上每个按钮都是一个命令
          • (1)遥控板:
          • (2)实现命令
          • (3)测试遥控器
        • 示例二:添加撤销功能
          • (1)改造命令接口,添加一个撤销功能
          • (2)所有的命令实现都要添加一个撤销功能
          • (3)遥控器上添加一个撤销按钮
          • (4)测试遥控器
        • 示例三:实现一个需要记录状态的撤销功能
      • 3.4 迭代器模式
        • (1)迭代器模式的示例
        • (2)迭代器Iterator接口两种常见的定义
        • (3)ArrayList使用迭代器在遍历的时候,调用`remove()`方法会不会报错
        • (4)支持快照的迭代器
      • 3.5 中介者模式
      • 3.6 观察者模式(发布订阅模式)
        • (1)观察者模式(自己实现)
        • (2)JDK自带的观察者模式
        • (3)Guava的EventBus
      • 3.7 备忘录模式
        • 示例一:撤销上一次输入
          • (1)版本一:违背了封装原则
          • (2)版本二:使用备忘录模式进行改造
      • 3.8 模版方法模式
        • (1)示例一:一个带有钩子的模版:
      • 3.9 状态模式
        • (1)第一个示例
          • 版本一:没有使用状态
          • 版本二:使用状态模式进行改造
        • (2)第二个示例:超级马里奥游戏
          • 版本一:分支逻辑法
          • 版本二:查表法
          • 版本三:状态模式
      • 3.10 策略模式
        • (1)示例一:策略模式的体现
        • (2)示例二:对一个文件内容进行排序的代码演进
          • 第一版:为使用设计模式之前
          • 第二版:定义出排序算法族(将策略的定义分离出来)
          • 第三版:使用工厂模式对算法进行封装
          • 第四版:借助查表法,去除if-else判断
      • 3.11 访问者模式
        • (1)访问者模式的演变示例:提取不同类型文件内容信息到txt文件
          • 版本一:不同子类负责不同的功能
          • 版本二:业务操作跟具体数据结构解耦,利用重载
          • 版本三:向访问者模式跨出重要一步
          • 版本四:将行为进行抽象出Visitor,完成访问者模式
        • (2)Single Dispatch 和 Double Dispatch
        • (3)使用工厂模式实现案例:提取不同类型文件内容信息到txt文件

一、设计模式的分类

设计模式要干的事情就是解耦,创建型模式是把创建和使用代码解耦,结构型模式是将不同够功能代码解耦,行为型模式是将不同的行为代码解耦。

设计原则和思想其实比设计模式更加普适重要,掌握了代码的设计原则和思想,我们甚至可以创造出来新的设计模式。

如果只掌握知识,没锻炼能力,遇到实际问题,遇到问题还是没法自己去分析、思考、解决。

1.1 创建型(五种)

  • 单例模式
  • 工厂模式
  • 抽象工厂模式
  • 建造器模式
  • 原型模式

记忆:一个单例,两个工厂,一个Builder,一个clone

1.2 结构型(七种)

  • 适配器模式
  • 装饰模式
  • 代理模式
  • 组合模式
  • 桥接模式
  • 享元模式
  • 外观模式

记忆:装成另一个角色(适配)、打扮自己是自己更突出(装饰)、让第三方代劳(代理)、合而为一(组合)、多个人搭成桥(桥接)、变成多个自己(享元模式)、做出一个按钮启动所有功能(外观)

1.3 行为型(十一种)

  • 责任链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 观察者模式
  • 备忘录模式
  • 模版模式
  • 状态模式
  • 策略模式
  • 参观者模式

记忆:领导命令(命令模式)拿出一盘大铁链(责任链),解开铁链(解释器),拉动铁链一圈圈的转动(迭代器模式),空中出现一块幕布媒介(中介者模式)、底下坐着众多的观者者(观察者模式),他们手里拿着备忘录(备忘录)、备忘录上有画好的模版(模版)、向里面填写敌军状态(状态)和应对策略(策略),这个时候大门外出现一个人,来视察现场的状态(参观者)。

二、创建型

2.1 单例模式

单例模式:一个类只允许创建一个对象。

使用场景;对象只需要存在一份,没必须每次使用都创造这个对象。

写日志的对象、配置信息

业务概念上,有些数据在系统中只应保存一份。单例还可以解决资源访问冲突的问题(写日志)

实现思路:1. 把构造函数私有化;2. 考虑线程安全;3. 是否延迟加载;4. getInstance时的性能;

代码实现的方案:懒汉式、饿汉式、静态内部类、枚举

(1)懒汉式:用到到时候再创建

/*** 懒汉式: 用到到时候再创建* @Date 2022/10/7* @Author lifei*/
public class Single01 {private Single01(){}/*** 使用volatile的作用:1. 禁止指令重排; 2. 变量不会再多个线程中存在多个副本,直接从主内粗读取* (volatile只在jdk1.5之后有用)**  instance = new Single01();  会被拆分成三个指令*  1. 为 instance分配内存空间;*  2. 调用 Single01 构造函数为其初始化;*  3. 将instance对象指向分配的内存空间;**  指令可以 1-2-3 顺序执行,如果指令重排 可能会以 1-3-2 顺序执行*  第一个线程执行 1-3 后,此时instance已经不为null,但还没有初始化,第二个线程抢用instance,就会出错*/private volatile static Single01 instance;public static Single01 instance() {if (instance==null) {synchronized(Single01.class) {if (instance==null) {instance = new Single01();}}}return instance;}
}

(2)饿汉式:类加载到时候,就把实例对像创建好

这样方式,在类加载的时候,就会执行。

如果我们的构造函数要依赖其它类干一些事情,就不能使用这种把对象的创建委托给类装载器的方式了。

/*** 饿汉式:提前创建好* @Date 2022/10/7* @Author lifei*/
public class Single02 {private Single02(){}private final static Single02 instance = new Single02();public static Single02 instance() {return instance;}
}

(3)静态内部类

只有在调用instance()的时候,才会把实例创建出来。

InstanceHolder是一个静态内部类,当外部类 Single03被加载的时候,并不会创建 InstanceHolder实例对象。只有当调用 instance() 方法时,InstanceHolder才会被加载,这个时候才会创建 instance。instance 的唯一性、创建过程的线程安全性,都由 JVM 来保证。所以,这种实现方法既保证了线程安全,又能做到延迟加载。

/*** @Date 2022/10/7* @Author lifei*/
public class Single03 {private Single03() {}private static class InstanceHolder {private static final Single03 instance = new Single03();}public static Single03 instance() {return InstanceHolder.instance;}
}

(4)枚举

/*** 枚举* @Date 2022/10/7* @Author lifei*/
public enum Single04Enum {INSTANCE;
}

单例模式的缺陷:

  • 对代码扩展性不好(从单例,变成多例);
  • 单例不支持有参数的构造函数;
  • 单例对代码的可测性不好;

唯一的范围:

  • 进程内唯一

  • 线程内唯一:使用Map

  • 集群环境下的唯一

    我们需要把这个单例对象序列化并存储到外部共享存储区(比如文件)。进程在使用这个单例对象的时候,需要先从外部共享存储区中将它读取到内存,并反序列化成对象,然后再使用,使用完成之后还需要再存储回外部共享存储区。

多例模式:一个类可以创建多个对象,但是个数是有限制的。也可以理解为,同一类型的只能创建一个对象,不同类型的可以创建多个对象。

2.2 工厂方法模式

所有工厂模式都是用来封装对象的创建。工厂方法模式通过让自类决定该创建的对象是什么。

工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

从简单工厂,到工厂方法模式,再到抽象工厂模式。

(1)初始代码:根据类型创建Pizza

public class Pizza {public void prepare() {System.out.println("prepare...");}public void bake() {System.out.println("bake...");}public void cut() {System.out.println("cut...");}public void box() {System.out.println("box....");}
}
/***  根据type创建不同种类的Pizza* @Date 2022/10/7* @Author lifei*/
public class PizzaStore {public Pizza orderPizza(String type) {Pizza pizza;if (type.equals("cheese")) {pizza = new CheesePizza();} else if (type.equals("veggie")) {pizza = new VeggiePizza();} else {pizza = new Pizza();}pizza.prepare();pizza.bake();pizza.cut();pizza.box();return pizza;}
}

(2)简单工厂:将创建Pizza的功能抽象出来,就变成了简单工厂

简单工厂不是一个设计模式,更像一种编程习惯。

/*** 简单工厂:不是一种设计模式,更像一种编程习惯* @Date 2022/10/7* @Author lifei*/
public class SimplePizzaFactory {public Pizza createPizza(String type) {Pizza pizza;if (type.equals("cheese")) {pizza = new CheesePizza();} else if (type.equals("veggie")) {pizza = new VeggiePizza();} else {pizza = new Pizza();}return pizza;}
}
***  根据type创建不同种类的Pizza* @Date 2022/10/7* @Author lifei*/
public class PizzaStore {private SimplePizzaFactory pizzaFactory;public PizzaStore(SimplePizzaFactory pizzaFactory) {this.pizzaFactory = pizzaFactory;}public Pizza orderPizza(String type) {/*Pizza pizza;if (type.equals("cheese")) {pizza = new CheesePizza();} else if (type.equals("veggie")) {pizza = new VeggiePizza();} else {pizza = new Pizza();}*/Pizza pizza = pizzaFactory.createPizza(type);pizza.prepare();pizza.bake();pizza.cut();pizza.box();return pizza;}
}

(3)工厂方法模式:在不同的地区开分店

这些不同的店,提供个性化的产品,但是也有相同规格的约束。因此对不同地区的店进行抽象:

public abstract class AbstractPizzaStore {// 个性化的产品由地区决定abstract Pizza createPizza(String type);public Pizza orderPizza(String type) {Pizza pizza = createPizza(type);pizza.prepare();pizza.bake();pizza.cut();pizza.box();return pizza;}
}

河南Pizza:

/*** 河南独特的Pizza* @Date 2022/10/7* @Author lifei*/
public class HeNanPizzaStore extends AbstractPizzaStore {@OverridePizza createPizza(String type) {Pizza pizza;if (type.equals("cheese")) {pizza = new HeNanCheesePizza();} else if (type.equals("veggie")) {pizza = new HeNanVeggiePizza();} else {pizza = new Pizza();}return pizza;}
}

天津Pizza:

/*** 河南独特的Pizza* @Date 2022/10/7* @Author lifei*/
public class TianJinPizzaStore extends AbstractPizzaStore {@OverridePizza createPizza(String type) {Pizza pizza;if (type.equals("cheese")) {pizza = new TianJinCheesePizza();} else if (type.equals("veggie")) {pizza = new TianJinVeggiePizza();} else {pizza = new Pizza();}return pizza;}
}

工厂方法是抽象的,依靠子类来处理对象的创建。

abstract Product factoryMethod(String type);

2.3 抽象工厂模式

提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指明具体的类。

应用:Spring 的依赖注入容器(DI)。

基于上面工厂方法模式:假如每个地区生产披萨所用的配料表不一样,那么披萨就要依赖配料工厂

为此,将其Pizza准备配料的方法抽象出来。

/*** 抽象工厂 中的接口类*/
public interface PizzaIngredientFactory {/*** 生产 配料01* @return*/String createIngredient01();/*** 生产 配料 02* @return*/String createIngredient02();}
/*** 抽象工厂的 使用* @Date 2022/10/7* @Author lifei*/
public abstract class AbstractPizza {// 配料protected String ingredient;// 配料抽象工厂protected PizzaIngredientFactory pizzaIngredientFactory;public AbstractPizza(PizzaIngredientFactory pizzaIngredientFactory) {this.pizzaIngredientFactory = pizzaIngredientFactory;}/*** 准备配料, 具体的*/public abstract void prepare();public void bake() {System.out.println("bake...");}public void cut() {System.out.println("cut...");}public void box() {System.out.println("box....");}
}

在具体的披萨中使用抽象工厂:

/*** 具体的Pizza 使用抽象工厂 * @Date 2022/10/7* @Author lifei*/
public class CheesePizza02 extends AbstractPizza{// 传递一个具体的配料工厂public CheesePizza02(PizzaIngredientFactory pizzaIngredientFactory) {super(pizzaIngredientFactory);}// 只使用配料01@Overridepublic void prepare() {this.ingredient01 = pizzaIngredientFactory.createIngredient01();}
}

一个具体的配料工厂:

/*** 抽象工厂的具体实现*/
public class HeNanPizzaIngredientFactory implements PizzaIngredientFactory{/*** 生产 配料01* @return*/@Overridepublic String createIngredient01() {return "HeNan ingredient01";}/*** 生产 配料 02* @return*/@Overridepublic String createIngredient02() {return "HeNan ingredient02";}}

抽象工厂模式中使用了工厂方法模式:

/*** 工厂方法模式中的 披萨商店* @Date 2022/10/7* @Author lifei*/
public abstract class AbstractPizzaStore02 {// 个性化的产品由地区决定abstract AbstractPizza createPizza(String type);public AbstractPizza orderPizza(String type) {AbstractPizza pizza = createPizza(type);pizza.prepare();pizza.bake();pizza.cut();pizza.box();return pizza;}
}
/*** 河南独特的Pizza* @Date 2022/10/7* @Author lifei*/
public class HeNanPizzaStore02 extends AbstractPizzaStore02 {@OverrideAbstractPizza createPizza(String type) {PizzaIngredientFactory pizzaIngredientFactory = new HeNanPizzaIngredientFactory();AbstractPizza pizza = null;if (type.equals("cheese")) {pizza = new CheesePizza02(pizzaIngredientFactory);}return pizza;}
}

2.4 构造器模式

构造器模式的好处:能够很好的扩展大量的可选参数。

与工厂模式的区别:工厂模式用于创建不同但相关类型的对象。构建者模式用来创建一种类型的复杂对象。

/*** 营养成分* @Date 2022/10/11* @Author lifei*/
public class NutritionFacts {private Integer servingSize;private Integer servings;private Integer calories;private Integer fat;private Integer sodium;private Integer carbohydrate;public NutritionFacts(){}private NutritionFacts(Builder builder){this.servingSize = builder.servingSize;this.servings = builder.servings;this.calories = builder.calories;this.fat = builder.fat;this.sodium = builder.sodium;this.carbohydrate = builder.carbohydrate;}public static Builder builder() {return new Builder();}public static class Builder {private Integer servingSize;private Integer servings;private Integer calories;private Integer fat;private Integer sodium;private Integer carbohydrate;public Builder servingSize(Integer servingSize) {this.servingSize = servingSize; return this;}public Builder servings(Integer servings) {this.servings = servings; return this;}public Builder calories(Integer calories) {this.calories = calories; return this;}public Builder fat(Integer fat) {this.fat = fat; return this;}public Builder sodium(Integer sodium) {this.sodium = sodium; return this;}public Builder carbohydrate(Integer carbohydrate) {this.carbohydrate = carbohydrate; return this;}public NutritionFacts build() {return new NutritionFacts(this);}}// 省略getter 和 setter 方法
}

使用

NutritionFacts nutritionFacts = NutritionFacts.builder().carbohydrate(1).servings(2).servingSize(3).fat(4).calories(5).sodium(6).build();

构造器模式还可以用于类的层次结构:

/*** 抽象的层级:* 子类于子类有共同的属性,也有不一样的属性* @Date 2022/10/11* @Author lifei*/
public abstract class AbstractBox {protected Integer size;protected String color;public AbstractBox(Builder builder) {this.size = builder.size;this.color = builder.color;}abstract static class Builder<T extends Builder<T>> {protected Integer size;protected String color;// 模拟的self参数:子类必须复写这个方法,返回"this"protected abstract T self();protected abstract AbstractBox build();public T size(Integer size) {this.size = size;return self();}public T color(String color) {return self();}}
}

一个子类:

/*** 智能盒子* @Date 2022/10/11* @Author lifei*/
public class ZnBox extends AbstractBox{private Boolean znFlag;public ZnBox(Builder builder) {super(builder);this.znFlag = builder.znFlag;}public static class Builder extends AbstractBox.Builder<Builder> {private Boolean znFlag;public Builder znFlag(Boolean znFlag) {this.znFlag = znFlag;return this;}@Overrideprotected Builder self() {return this;}// 协变返回类型:子类声明返回超级类中声明的返回类型的子类// 它允许客户端无需类型转换就能使用这些了类型@Overrideprotected ZnBox build() {return new ZnBox(this);}}
}

使用:

ZnBox znBox = new ZnBox.Builder().color("red").size(20).znFlag(true).build();

2.5 原型模式

使用场景:当创建给定的实例的过程很昂贵或很复杂的时候,可以利用对已有对象(原型)进行复制(或叫做拷贝)的方式来创建新对象。这种基于原型来创建对象的方式就叫做原型设计模式,简称原型模式。

如果对象的创建需要经过复杂的计算(比如排序、计算哈希值),或者需要从非常慢的IO中读取(比如RPC、网络、数据库、文件系统等),这种情况就可以使用原型模式。

在Java中,Object类的clone() 方法执行的是我们刚刚说的浅拷贝。

它们只会拷贝对象中的基本数据类型,以及引用对象的内存地址,不会递归的拷贝引用对象本身。

/*** 原型模式* @Date 2022/10/16* @Author lifei*/
public class PhoneNumber implements Cloneable{private String userName;private int[] numbers;/*** clone方法的通用约定: 这个方法返回的对象应该通过调用 super.clone 获取*     如果类的clone方法返回的实例不是通过调用super.clone 方法获得,而是通过调用构造七获得,编译器就不会发出警告,*     但是该类的子类调用了super.clone方法,得到的对象就会拥有错误的类,并阻止了clone方法的子类正常工作。* 协变返回类型: Object 的clone方法 返回的是 Object,但是这个clone方法返回的却是PhoneNumber。** @return*/@Overridepublic PhoneNumber clone() {try {PhoneNumber phoneNumber = (PhoneNumber) super.clone();phoneNumber.numbers = numbers.clone();return phoneNumber;} catch (CloneNotSupportedException e) {throw new AssertionError(e);}}// 省略 getter 和 setter 方法
}

另一种深拷贝一个对象的思路是:先序列化,再反序列化。

        try {PhoneNumber phoneNumber = new PhoneNumber();phoneNumber.setUserName("小明");phoneNumber.setNumbers(new int[]{1, 0, 5, 1, 1});// 先进行序列化ByteArrayOutputStream bo = new ByteArrayOutputStream();ObjectOutputStream oo = new ObjectOutputStream(bo);oo.writeObject(phoneNumber);// 再进行反序列化ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());ObjectInputStream oi = new ObjectInputStream(bi);Object o = oi.readObject();System.out.println(o);} catch (Exception e) {throw new RuntimeException(e);}

三、结构型

3.1 适配器模式(adapter)

适配器模式:将一个类的接口,转换成客户期待的另一个接口。适配器让原本接口不兼容的类可以合作无间。比如,现实中,把USB插头转变成type-C插头。

将不兼容的接口转变为兼容的接口。是一种事后补救的策略。

场景:将另一个系统的功能集成到当前的系统中,既不能改变另一个系统的代码,又不能改变当前系统的代码。

适配器,就像一个转接头。

适配器模式分为:对象适配器(使用对象的组合)、类适配器。

java不支持多继承,因此不支持类适配。但某些场景下可以通过 extends Adaptee implements ITarget 来达到类适配。

对象适配的示例:

/*** 将迭代器适配成枚举* @Date 2022/10/16* @Author lifei*/
public class EnumerationAdapter<T> implements Enumeration<T> {private Iterator<T> iterator;public EnumerationAdapter(Iterator<T> iterator) {this.iterator = iterator;}@Overridepublic boolean hasMoreElements() {return iterator.hasNext();}@Overridepublic T nextElement() {return iterator.next();}
}
    public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("01");list.add("03");list.add("02");EnumerationAdapter<String> adapter = new EnumerationAdapter<>(list.iterator());while (adapter.hasMoreElements()) {System.out.println(adapter.nextElement());}}

类适配:

public class Adaptee {public void a1() {System.out.println("a1");}public void b1(){System.out.println("b1");}public void cc() {System.out.println("cc");}
}public interface ITarget {void a2();void b2();void cc();
}public class Adapter extends Adaptee implements ITarget {@Overridepublic void a2() {super.a1();}@Overridepublic void b2() {super.b1();}
}

3.2 装饰者模式

通过组合的方式。

装饰者模式:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

装饰器模式相对于简单的组合关系,有两个比较特殊的地方:

  1. 装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类;
  2. 装饰器类是对功能的增强;

应用:Java的IO

父类:

/*** 一个咖啡品牌店* @Date 2022/10/16* @Author lifei*/
public abstract class Beverage {protected String description = "Unknown Beverage";public String getDescription() {return description;}public abstract double cost();
}

子类:某一个饮品

/*** 某一个饮品* @Date 2022/10/16* @Author lifei*/
public class Espresso extends Beverage{public Espresso() {this.description = "Espresso";}@Overridepublic double cost() {return 0.89;}
}

扩展:为饮品添加配料(比如:加糖、加冰、加牛奶等)

配料装饰器:

/*** 调料装饰器* @Date 2022/10/16* @Author lifei*/
public abstract class CondimentDecorator extends Beverage{public abstract String getDescription();
}

添加摩卡配料:

/*** 添加摩卡配料* @Date 2022/10/16* @Author lifei*/
public class Mocha extends CondimentDecorator{public Beverage beverage;public Mocha(Beverage beverage) {this.beverage = beverage;}@Overridepublic double cost() {return 0.20 + beverage.cost();}@Overridepublic String getDescription() {return beverage.getDescription() + ", Mocha";}
}

使用:

/*** 装饰器的示例* @Date 2022/10/16* @Author lifei*/
public class DecoratorDemo {public static void main(String[] args) {Beverage beverage = new Espresso();// Espresso, 价格为: 0.89System.out.println(beverage.getDescription() + ", 价格为: " + beverage.cost());// 添加摩卡beverage = new Mocha(beverage);// Espresso, Mocha, 价格为: 1.09System.out.println(beverage.getDescription() + ", 价格为: " + beverage.cost());}
}

比如,适配器模式再JavaIO 中的应用:

            InputStream inputStream = new FileInputStream("/DecoratorDemo.java");BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);LineNumberInputStream lineNumberInputStream = new LineNumberInputStream(bufferedInputStream);

3.3 代理模式

代理:控制和管理访问。

使用代理的常见情况:

  • 远程代理:

    一个简单的RPC框架。

  • 虚拟代理:需要创建开销很大的对象。

  • 保护代理:可以通过动态代理实现。

远程代理和虚拟代理:代理对象实现了真实对象的抽象接口,并持有真实对象的引用。

动态代理:有两种实现方式,一种是JDK 基于接口的动态代理;一种是cglab ,基于继承的动态代理。

JDK 的 java.lang.reflect,基于接口。

如果要代理的类作为一个普通的类,没有接口,那么Java的动态代理就没法使用了。

/*** 主题接口* @Date 2022/10/17* @Author lifei*/
public interface PersonBean {String getName();void setName(String name);
}
/*** 主题的一个实现* @Date 2022/10/17* @Author lifei*/
public class PersonBeanImpl implements PersonBean{private String name;@Overridepublic String getName() {return name;}@Overridepublic void setName(String name) {this.name = name;}
}

代理对象的执行方法:

/*** 代理对象的执行* @Date 2022/10/17* @Author lifei*/
public class OwnerInvocationHandler implements InvocationHandler {private PersonBean personBean;public OwnerInvocationHandler(PersonBean personBean) {this.personBean = personBean;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (method.getName().startsWith("get")) {return method.invoke(personBean, args);}else if (method.getName().startsWith("set")){throw new IllegalAccessException();}return null;}
}

创建代理对象:

    /*** 创建代理对象* @param personBean* @return*/private static PersonBean getOwnerProxy(PersonBean personBean) {return (PersonBean) Proxy.newProxyInstance(personBean.getClass().getClassLoader(), personBean.getClass().getInterfaces(),new OwnerInvocationHandler(personBean));}

测试:

    public static void main(String[] args) {PersonBean personBean = new PersonBeanImpl();personBean.setName("001");PersonBean ownerProxy = getOwnerProxy(personBean);System.out.println(ownerProxy.getName());ownerProxy.setName("002"); // 报错}

CGLIB动态代理,动态的生成一个要代理的子类。

CGLIB的缺点是,对final方法无法处理。

/*** 目标类* @Date 2022/10/17* @Author lifei*/
public class TargetObject {public void a() {System.out.println("方法a 执行了");b();}public void b() {System.out.println("方法b执行了");}
}
/*** 方法拦截* @Date 2022/10/17* @Author lifei*/
public class TargetInterceptor implements MethodInterceptor {private TargetObject targetObject;public TargetInterceptor(TargetObject targetObject) {this.targetObject = targetObject;}/**** @param o 目标对象* @param method 目标对象方法* @param params 目标对象方法参数* @param methodProxy CGLIB代理方法对象* @return* @throws Throwable*/@Overridepublic Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {System.out.println("方法调用前");// invoke 与 invokeSuper 的区别: https://www.cnblogs.com/lvbinbin2yujie/p/10284316.html// invoke 调用的对象是没有增强过,invokeSuper调用的雕像是增强过
//        Object result = methodProxy.invokeSuper(o, params);
//        Object result = methodProxy.invokeSuper(targetObject, params);  // 会报错Object result = methodProxy.invoke(targetObject, params);
//        Object result = methodProxy.invoke(o, params);  // 获报错System.out.println("方法调用后");return result;}
}

创建代理对象,及使用:

/*** 演示CGLIB* @Date 2022/10/17* @Author lifei*/
public class DemoCGLIB {public static void main(String[] args) {TargetObject target = new TargetObject();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(TargetObject.class);enhancer.setCallback(new TargetInterceptor(target));TargetObject targetObject = (TargetObject) enhancer.create();targetObject.a();}
}

3.4 桥接模式

桥接模式:将抽象与实现解耦,让它们可以独立变化。

应用:JDBC驱动是桥接模式的典型应用。

JDBC中,抽象是一套“类库”,具体的Driver(比如,com.mysql.jdbc.Driver)就相当于实现。这里的实现,并非接口的实现,而是跟具体数据库相关的一套“类库”。JDBC和Driver独立开发,通过对象之间的组合关系,组装在一起。JDBC的多有逻辑操作,最终都委托给Driver来执行。

3.5 享元模式

享元:共享的单元。

享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。

当一个系统存在大量重复对象的时候,如果这些重复对象是不可变对象。可以利用享元模式将对象设计为享元,在内存中只保留一份实例,供多处代码引用。

享元模式与单例模式的对比:在单例模式中,一个类只能创建一个对象。而在享元模式中,一个类可以创建多个对象,每个对象被多处代码引用共享。(享元模式有点类似,单例的变体:多例)。

享元模式与缓存:在享元模式中,通过工厂类来“缓存”已经创建好的对象。和我们平时所说的缓存“数据库缓存”、“CPU缓存”、“MemCache缓存”是两回事。平时所说的缓存主要是为了提高访问效率,而非复用。

享元模式与池化技术(连接池、线程池)的区别:池化技术的“复用”,可以理解为重复使用,主要是为了节约时间(不需要重新创建),在任一时刻,是被一是使用者独占,使用完了,放回迟中,再由其他使用者重复使用。享元模式中的“复用”,可以理解为“共享使用”,在整个生命周期中,都是被所有使用者共享的,主要目的是节省空间。

应用:

享元模式在Java Integer中的使用:

当我们通过自动装箱,也就是调用 valueOf() 来创建 Integer 对象的时候,如果要创建的 Integer 对象的值在 -128 到 127 之间,会从 IntegerCache 类中直接返回,否则才调用 new 方法创建。

JDK 也提供了方法来让我们可以自定义缓存的最大值,JDK 并没有提供设置最小值的方法。

//方法一:
-Djava.lang.Integer.IntegerCache.high=255
//方法二:
-XX:AutoBoxCacheMax=255

除了Integer ,其它的包装器类型,比如Long、Short、Byte等,也都利用了享元模式来缓存 -128 到 127 之间的数据。

        Integer val01 = Integer.valueOf(20);Integer val02 = Integer.valueOf(20);Integer val03 = new Integer(20);System.out.println(val01 == val02); // falseSystem.out.println(val01 == val03); // ture

构造函数创建的方式,不会用到IntegerCache。

享元模式在Java String中的使用:

        String str01 = "你好";String str02 = "你好";String str03 = new String("你好");System.out.println(str01==str02); // trueSystem.out.println(str01==str03); // false

String 类利用享元模式来复用相同的字符串常量。JVM 会专门开辟一块存储区来存储字符串常量,这块存储区叫作“字符串常量池”。类似于 Integer 中的 IntegerCache。不过,跟 IntegerCache 不同的是,它并非事先创建好需要共享的对象,而是在程序的运行期间,根据需要来创建和缓存字符串常量。

软引用、弱引用、Weakhashmap。

https://www.baeldung.com/java-weakhashmap

https://www.baeldung.com/java-soft-references

https://www.baeldung.com/java-weak-reference

3.6 外观模式

外观模式,又叫门面模式:为子系统提供一组统一的接口,定义一组高层接口让子系统更容易用。

外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

外观模式和“最少知识”原则(迪米特法则)的关系:只和你的密友谈话。

最少知识原则:每个模块(unit)只应该了解那些与它关系密切的模块(units: only units “closely” related to the current unit)的有限知识(knowledge)。或者说,每个模块只和自己的朋友“说话”(talk),不和陌生人“说话”(talk)。

3.7 组合模式

组合模式常和迭代器模式一起使用。

组合模式,主要用于处理树形结构数据。

将一组对象(文件和目录)组织成树形结构,以表示一种‘部分 - 整体’的层次结构(目录与子目录的嵌套结构)。组合模式让客户端可以统一单个对象(文件)和组合对象(目录)的处理逻辑(递归遍历)

将一组对象(员工和部门)组织成树形结构,以表示一种‘部分 - 整体’的层次结构(部门与子部门的嵌套结构)。组合模式让客户端可以统一单个对象(员工)和组合对象(部门)的处理逻辑(递归遍历)

(1)示例一:文件和目录

/*** 目录和文件的抽象* @Date 2022/10/20* @Author lifei*/
public abstract class FileSystemNodeParent {protected String path;public FileSystemNodeParent(String path) {this.path = path;}public abstract int countNumberOfFiles();public abstract long countSizOfFiles();public String getPath() {return path;}
}
/*** 目录的实现* @Date 2022/10/20* @Author lifei*/
public class DirectoryItem extends FileSystemNodeParent{private List<FileSystemNodeParent> subNodes = new ArrayList<>();public DirectoryItem(String path) {super(path);}@Overridepublic int countNumberOfFiles() {int res = 0;for (FileSystemNodeParent fileDir : subNodes) {res += fileDir.countNumberOfFiles();}return res;}@Overridepublic long countSizOfFiles() {long res = 0;for (FileSystemNodeParent fileDir : subNodes) {res += fileDir.countSizOfFiles();}return res;}public void addSubNode(FileSystemNodeParent fileSystemNode) {this.subNodes.add(fileSystemNode);}public void removeSubNode(FileSystemNodeParent fileSystemNode) {int size = subNodes.size();int i = 0;for (; i<subNodes.size(); i++) {if (StringUtils.equals(subNodes.get(i).getPath(), fileSystemNode.path)) {break;}}if (i<subNodes.size()) {subNodes.remove(i);}}
}
/*** 文件的实现* @Date 2022/10/20* @Author lifei*/
public class FileItem extends FileSystemNodeParent {public FileItem(String path) {super(path);}@Overridepublic int countNumberOfFiles() {return 1;}@Overridepublic long countSizOfFiles() {File file = new File(path);if (!file.exists()) {return 0l;}return file.length();}
}
/*** 组合模式的测试: 文件-目录 对象,使用组合模式* @Date 2022/10/20* @Author lifei*/
public class FileTreeDemo {public static void main(String[] args) {String dirPath = "/Users/lifei/Documents/workspace/githubRepositoies/JavaNoneRebuild/projects/javaActionPro/action-design-patterns/src";DirectoryItem directoryItem = createDirectoryItem(dirPath);System.out.println("文件的数量:" + directoryItem.countNumberOfFiles());System.out.println("文件的大小:" + directoryItem.countSizOfFiles());}/*** 根据根目录创建,一个文件树* @param dirPath* @return*/public static DirectoryItem createDirectoryItem(String dirPath) {File file = new File(dirPath);if (StringUtils.isBlank(dirPath) || !file.exists() || file.isFile()) {throw new RuntimeException("必须传递一个目录");}DirectoryItem directoryItem = new DirectoryItem(dirPath);String[] fileNames = file.list();for (String fileName : fileNames) {String subFilePath = dirPath + File.separator + fileName;File subFile = new File(subFilePath);if (subFile.isFile()) {directoryItem.addSubNode(new FileItem(subFilePath));}else {directoryItem.addSubNode(createDirectoryItem(subFilePath));}}return directoryItem;}
}

(2)示例二:组合模式和迭代器模式一块使用

组合模式的抽象:包含叶子和非叶子结点的所有方法

/*** 菜单组件:包含了叶子和组合 的所有方法* @Date 2022/10/29* @Author lifei*/
public abstract class MenuComponent {public void add(MenuComponent menuComponent) {throw new UnsupportedOperationException();}public void remove(MenuComponent menuComponent) {throw new UnsupportedOperationException();}public void getChild(int i) {throw new UnsupportedOperationException();}public String getName() {throw new UnsupportedOperationException();}public String getDescription() {throw new UnsupportedOperationException();}public boolean isVegetarian() {throw new UnsupportedOperationException();}public double getPrice() {throw new UnsupportedOperationException();}public void print() {throw new UnsupportedOperationException();}
}

叶子结点:

/*** 菜单项* @Date 2022/10/29* @Author lifei*/
public class MeanItem extends MenuComponent{private final String name;private final String description;private final boolean vegetarian;private final double price;public MeanItem(String name, String description, boolean vegetarian, double price) {this.name = name;this.description = description;this.vegetarian = vegetarian;this.price = price;}@Overridepublic String getName() {return name;}@Overridepublic String getDescription() {return description;}@Overridepublic boolean isVegetarian() {return vegetarian;}@Overridepublic double getPrice() {return price;}@Overridepublic void print() {String res = MoreObjects.toStringHelper(MeanItem.class).add("name", name).add("description", description).add("vegetarian", vegetarian).add("price", price).toString();System.out.println(res);}
}

非叶子结点:

/*** 菜单* @Date 2022/10/29* @Author lifei*/
public class Menu extends MenuComponent{private List<MenuComponent> menuComponents = new ArrayList<>();private String name;private String description;public Menu(String name, String description) {this.name = name;this.description = description;}public List<MenuComponent> getMenuComponents() {return menuComponents;}@Overridepublic String getName() {return name;}@Overridepublic String getDescription() {return description;}@Overridepublic void add(MenuComponent menuComponent) {this.menuComponents.add(menuComponent);}@Overridepublic void remove(MenuComponent menuComponent) {this.menuComponents.remove(menuComponent);}@Overridepublic void getChild(int i) {this.menuComponents.get(i);}@Overridepublic void print() {String res = MoreObjects.toStringHelper(Menu.class).add("name", name).add("description", description).toString();System.out.println(res);System.out.println("-----------------");Iterator<MenuComponent> iterator = menuComponents.iterator();while (iterator.hasNext()) {MenuComponent menuComponent = iterator.next();menuComponent.print();}}
}

使用组合的抽象:

/*** 服务员* @Date 2022/10/29* @Author lifei*/
public class Waitress {private MenuComponent menuComponent;public Waitress(MenuComponent menuComponent) {this.menuComponent = menuComponent;}public void printMenu() {menuComponent.print();}
}

演示组合模式:

/*** 示例:组合模式 + 迭代器模式* @Date 2022/10/29* @Author lifei*/
public class ApplicationDemo {public static void main(String[] args) {MenuComponent allMenus = new Menu("所有的菜单", "所有的菜单组合");// 一级菜单MenuComponent pancakeHouseMenu = new Menu("煎饼屋", "早餐");MenuComponent dinerMenu = new Menu("大餐", "晚餐");MenuComponent cafeMenu = new Menu("咖啡屋", "饮品");allMenus.add(pancakeHouseMenu);allMenus.add(dinerMenu);allMenus.add(cafeMenu);// 二级菜单pancakeHouseMenu.add(new MeanItem("Pancake01", "001",  false, 2.99));pancakeHouseMenu.add(new MeanItem("Pancake02", "002",  true, 3.99));pancakeHouseMenu.add(new MeanItem("Pancake03", "003",  false, 1.99));pancakeHouseMenu.add(new MeanItem("Pancake04", "004",  true, 5.99));Waitress waitress = new Waitress(allMenus);waitress.printMenu();}
}

三、行为型

3.1 职责链模式

职责链模式:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一个链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。

(1)职责链的第一种实现方式:用链表存放处理器

定义一个抽象的Handler,相当于职责链上的一个节点。

/*** 职责链模式:第一种实现方案* 定义一个抽象的Handler,handler 是抽象方法。*     1. 每个处理器 handle() 函数的代码结构类似。*     2. 如果它能处理请求,就bu不继续向下传递;*     3. 如果它不能处理,则由后面的处理器来处理(也就是调用 successor.handler() 来处理)* @Date 2022/10/23* @Author lifei*/
public abstract class Handler {protected Handler successor = null;public void setSuccessor(Handler successor) {this.successor = successor;}public abstract void handle();
}/*** 处理器A* @Date 2022/10/23* @Author lifei*/
public class AHandler extends Handler{@Overridepublic void handle() {boolean handled = false;//.... 判断当前处理其链是否可以操作,如果可以操作将 handled设置为trueif (!handled && successor!=null) {successor.handle();}}
}/*** 处理器B* @Date 2022/10/23* @Author lifei*/
public class BHandler extends Handler{@Overridepublic void handle() {boolean handled = false;//...... 判断当前处理其链是否可以操作,如果可以操作将 handled设置为trueif (!handled && successor!=null) {successor.handle();}}
}

定义处理器链的结构:HandlerChain

/*** 处理器链:*  从数据结构的角度来看,它就是一个记录了链头、链尾的链表。*  其中,记录链尾,是为了方便添加处理器* @Date 2022/10/23* @Author lifei*/
public class HandlerChain {private Handler head, tail;/*** 添加处理器* @param handler*/public void addHandler(Handler handler) {handler.setSuccessor(null);if (head==null) {head = handler;tail = handler;return;}tail.setSuccessor(handler);tail = handler;}public void handle() {if (head!=null) {head.handle();}}
}

在上面的实现中,AHandler和BHandler的handle()方法有共同的模版,因此可以使用模版方法模式进行优化:

public abstract class Handler {protected Handler successor = null;//    public abstract void handle();public final void handle() {boolean handled = doHandle();if (!handled && Objects.nonNull(successor)) {successor.handle();}}protected abstract boolean doHandle();public void setSuccessor(Handler successor) {this.successor = successor;}
}public class AHandler extends Handler{@Overrideprotected boolean doHandle() {boolean handled = false;// 看是否能在当前处理器处理,如果能成功处理,返回true,否则返回false。return handled;}
}public class BHandler extends Handler{@Overrideprotected boolean doHandle() {boolean handled = false;// 看是否能在当前处理器处理,如果能成功处理,返回true,否则返回false。return handled;}
}

使用举例:

/*** 使用举例* @Date 2022/10/23* @Author lifei*/
public class Application {public static void main(String[] args) {HandlerChain handlerChain = new HandlerChain();handlerChain.addHandler(new AHandler());handlerChain.addHandler(new BHandler());handlerChain.handle();}
}

(2)职责链的第二种实现方式:用数组存放处理器

责任链的节点:处理器

public interface IHandler {boolean handle();
}public class HandlerA implements IHandler{@Overridepublic boolean handle() {boolean handled = false;// ...... 判断当前处理其是否能处理,如果能处理返回ture,否则,返回falsereturn handled;}
}public class HandlerB implements IHandler{@Overridepublic boolean handle() {boolean handled = false;// ...... 判断当前处理其是否能处理,如果能处理返回ture,否则,返回falsereturn handled;}
}
/*** 处理器链的第二种实现方案:使用数组来存放处理器* @Date 2022/10/23* @Author lifei*/
public class HandlerChain {private List<IHandler> handlers = new ArrayList<>();public void addHandler(IHandler handler) {this.handlers.add(handler);}public void handle() {for (IHandler handler : handlers) {boolean handled = handler.handle();if (handled) {break;}}}
}
/*** 使用举例* @Date 2022/10/23* @Author lifei*/
public class Application {public static void main(String[] args) {HandlerChain handlerChain = new HandlerChain();handlerChain.addHandler(new HandlerA());handlerChain.addHandler(new HandlerB());handlerChain.handle();}
}

(3)职责链的一种变种:请求会被所有的处理器都处理一遍,不存在中途终止的情况

这种变种只需要稍微修改,把上面的终止条件去掉。

用链表存放处理器,的处理器

public abstract class Handler {protected Handler successor = null;public final void handle() {doHandle();if (Objects.nonNull(successor)) {successor.handle();}}protected abstract void doHandle();public void setSuccessor(Handler successor) {this.successor = successor;}
}

用数组存放处理器的处理器链:

/*** 处理器链的第二种实现方案:使用数组来存放处理器* @Date 2022/10/23* @Author lifei*/
public class HandlerChain {private List<IHandler> handlers = new ArrayList<>();public void addHandler(IHandler handler) {this.handlers.add(handler);}public void handle() {for (IHandler handler : handlers) {if (Objects.nonNull(handler)) {handler.handle();}}}
}

(4)应用场景:论谈发帖审核

/*** 敏感词过滤* @Date 2022/10/23* @Author lifei*/
public abstract class SensitiveWordFilter {private SensitiveWordFilter successor;public void setSuccessor(SensitiveWordFilter successor) {this.successor = successor;}public final boolean filter(Context context) {boolean legal = doFilter(context);if (legal && Objects.nonNull(successor)) {return successor.filter(context);}return legal;}public abstract boolean doFilter(Context context);
}/*** 性关键词过滤* @Date 2022/10/23* @Author lifei*/
public class SexyWordFilter extends SensitiveWordFilter{@Overridepublic boolean doFilter(Context context) {boolean legal = false;// 判断是否合法return legal;}
}/*** 政治敏感词过滤* @Date 2022/10/23* @Author lifei*/
public class PoliticalWordFilter extends SensitiveWordFilter{@Overridepublic boolean doFilter(Context context) {boolean legal = false;// 过滤政治敏感词return legal;}
}/*** 广告法敏感词过滤* @Date 2022/10/23* @Author lifei*/
public class AdsWordFilter extends SensitiveWordFilter{@Overridepublic boolean doFilter(Context context) {boolean legal = false;// 看是否符合广告法return legal;}
}

过滤器链:

/*** 敏感词过滤链* @Date 2022/10/23* @Author lifei*/
public class SensitiveWordFilterChain {private SensitiveWordFilter head, tail;public void addSensitiveWordFilter(SensitiveWordFilter sensitiveWordFilter) {sensitiveWordFilter.setSuccessor(null);if (Objects.nonNull(head)) {this.head = sensitiveWordFilter;this.tail = sensitiveWordFilter;return;}this.tail.setSuccessor(sensitiveWordFilter);this.tail = sensitiveWordFilter;}public boolean filter(Context context) {boolean legal = true;if (Objects.nonNull(head)) {legal = head.filter(context);}return legal;}
}
/*** 应用示例:过滤敏感词* @Date 2022/10/23* @Author lifei*/
public class ApplicationSensitiveWordFilter {public static void main(String[] args) {SensitiveWordFilterChain sensitiveWordFilterChain = new SensitiveWordFilterChain();sensitiveWordFilterChain.addSensitiveWordFilter(new SexyWordFilter());sensitiveWordFilterChain.addSensitiveWordFilter(new PoliticalWordFilter());sensitiveWordFilterChain.addSensitiveWordFilter(new AdsWordFilter());boolean legal = sensitiveWordFilterChain.filter(new Context());if (legal) {// 发表} else {// 不发表}}
}

(5)职责链在开发框架中的应用:过滤器(Servlet Filter)

Servlet Filter是Servlet规范的一部分,实现依赖于Web容器。

(6)职责链在开发框架中的应用:拦截器(Spring Interceptor)

Spring Interceptor是Spring MVC框架的一部分,由Spring MVC框架来提供实现的。

客户端发送的请求,会先经过Servlet Filter,然后再经过Spring Interceptor,最后到达具体的业务代码中。

(7)AOP(面向切面)、Servlet Filter(过滤器)、Spring Intercepter(拦截器)都可以做访问控制功能,区别是什么?

servlet filter 作用于容器,应用范围影响很大;Spring Intercepter (拦截器)作用于框架,范围影响适中;AOP 作用于业务逻辑,精细化处理,范围影响很小。

3.2 解释器模式

解释器模式:为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。

(1)示例一:根据计算规则,获取输入内容的结果

版本一:不使用解释器模式

如果表达式很复杂,这个类就会很大。

/*** 未使用解释器模式*   假设我们定义了一个新的加减乘除计算“语言”,语法规则如下:*   1. 运算符只包含加、减、乘、除,并且没有优先级的概念;*   2. 表达式(也就是前面提到的“句子”)中,先书写数字,后书写运算符,空格隔开;*   3. 按照先后顺序,取出两个数字和一个运算符计算结果,结果重新放入数字的最头部位置,*      循环上述过程,直到只剩下一个数字,这个数字就是表达式最终的计算结果。* @Date 2022/11/5* @Author lifei*/
public class ExpressionInterpreter {public long interpreter(String expression) {Deque<Long> deque = new LinkedList<>();String[] elements = expression.split(" ");int lastNumIndex = elements.length/2;// 拿到数字for (int i=0; i<=lastNumIndex; i++) {deque.add(Long.parseLong(elements[i]));}// 获取运算符for (int i=lastNumIndex+1; i<elements.length; i++) {String operator = elements[i];boolean valid = StringUtils.equals(operator, "+") || StringUtils.equals(operator, "-")|| StringUtils.equals(operator, "*") || StringUtils.equals(operator, "/");if (!valid) {throw new IllegalArgumentException("无效的表达式:" + expression);}long firstVal =  deque.pollFirst();long secondVal = deque.pollFirst();long res = 0;if (StringUtils.equals(operator, "+")) {res = firstVal + secondVal;}else if (StringUtils.equals(operator, "-")) {res = firstVal - secondVal;}else if (StringUtils.equals(operator, "*")) {res = firstVal * secondVal;} else {res = firstVal/secondVal;}deque.addFirst(res);}if (deque.size()!=1) {throw new RuntimeException("无效的表达式:" + expression);}return deque.pop();}
}
版本二:使用解释器模式进行重构
/*** 表达式接口* @Date 2022/11/5* @Author lifei*/
public interface Expression {long interpreter();
}/*** 数值表达式* @Date 2022/11/5* @Author lifei*/
public class NumberExpression implements Expression{private long number;public NumberExpression(long number) {this.number = number;}public NumberExpression(String number) {this.number = Long.parseLong(number);}@Overridepublic long interpreter() {return number;}
}/*** 加法表达式* @Date 2022/11/5* @Author lifei*/
public class AdditionExpression implements Expression {private Expression expression1;private Expression expression2;public AdditionExpression(Expression expression1, Expression expression2) {this.expression1 = expression1;this.expression2 = expression2;}@Overridepublic long interpreter() {return expression1.interpreter() + expression2.interpreter();}
}/*** 减法表达式* @Date 2022/11/5* @Author lifei*/
public class SubtractionExpression implements Expression {private Expression expression1;private Expression expression2;public SubtractionExpression(Expression expression1, Expression expression2) {this.expression1 = expression1;this.expression2 = expression2;}@Overridepublic long interpreter() {return expression1.interpreter() - expression2.interpreter();}
}/*** 乘法表达式* @Date 2022/11/5* @Author lifei*/
public class MultiplicationExpression implements Expression {private Expression expression1;private Expression expression2;public MultiplicationExpression(Expression expression1, Expression expression2) {this.expression1 = expression1;this.expression2 = expression2;}@Overridepublic long interpreter() {return expression1.interpreter() * expression2.interpreter();}
}/*** 除法表达式* @Date 2022/11/5* @Author lifei*/
public class DivisionExpression implements Expression {private Expression expression1;private Expression expression2;public DivisionExpression(Expression expression1, Expression expression2) {this.expression1 = expression1;this.expression2 = expression2;}@Overridepublic long interpreter() {return expression1.interpreter() / expression2.interpreter();}
}

(2)示例二:监视器中的自定义警告规则

/*** 自定义一个告警规则:* 1. 只包含“||、&&、>、<、==”这五个运算符;* 2. “>、<、==”运算符的优先级高于“||、&&”运算符,“&&”运算符优先级高于“||”* @Date 2022/11/5* @Author lifei*/
public class DemoTest {public static void main(String[] args) {String rule = "key1 > 100 && key2 < 30 || key3 < 100 || key4 == 88";AlertRuleInterpreter interpreter = new AlertRuleInterpreter(rule);Map<String, Long> state = new HashMap<>();state.put("key1", 101l);state.put("key3", 101l);state.put("key4", 81l);boolean alert = interpreter.interpreter(state);System.out.println(alert);}
}

表达式:

public interface RuleExpression {boolean interpreter(Map<String, Long> state);
}/*** 大于表达式* @Date 2022/11/5* @Author lifei*/
public class RuleGTExpression implements RuleExpression{private String key;private Long value;public RuleGTExpression(String expression) {String[] elements = expression.trim().split(" ");if (elements.length!=3 && !StringUtils.equals(elements[1], ">")) {throw new IllegalArgumentException("无效的表达式: " + expression);}this.key = elements[0];this.value = Long.parseLong(elements[2]);}@Overridepublic boolean interpreter(Map<String, Long> state) {if (!state.containsKey(key)) {return false;}return state.get(key) > value;}
}/*** 小于表达式* @Date 2022/11/5* @Author lifei*/
public class RuleLTExpression implements RuleExpression{private String key;private Long value;public RuleLTExpression(String expression) {String[] elements = expression.trim().split("\\s+");if (elements.length!=3 && !StringUtils.equals(elements[1], "<")) {throw new IllegalArgumentException("无效的表达式: " + expression);}this.key = elements[0];this.value = Long.parseLong(elements[2]);}@Overridepublic boolean interpreter(Map<String, Long> state) {if (!state.containsKey(key)) {return false;}return state.get(key) < value;}
}/*** 等于表达式* @Date 2022/11/5* @Author lifei*/
public class RuleEQExpression implements RuleExpression{private String key;private Long value;public RuleEQExpression(String expression) {String[] elements = expression.trim().split("\\s+");if (elements.length!=3 && !StringUtils.equals(elements[1], "==")) {throw new IllegalArgumentException("无效的表达式: " + expression);}this.key = elements[0];this.value = Long.parseLong(elements[2]);}@Overridepublic boolean interpreter(Map<String, Long> state) {if (!state.containsKey(key)) {return false;}return state.get(key) == value;}
}/*** && 表达式* @Date 2022/11/5* @Author lifei*/
public class RuleAndExpression implements RuleExpression{private List<RuleExpression> ruleExpressionList = new ArrayList<>();public RuleAndExpression(String expression) {String[] subExpressions = expression.trim().split("&&");for (String subExpression : subExpressions) {if (subExpression.contains(">")) {ruleExpressionList.add(new RuleGTExpression(subExpression));} else if (subExpression.contains("<")) {ruleExpressionList.add(new RuleLTExpression(subExpression));} else if (subExpression.contains("==")) {ruleExpressionList.add(new RuleEQExpression(subExpression));} else {throw new IllegalArgumentException("无效的表达式: " + expression);}}}@Overridepublic boolean interpreter(Map<String, Long> state) {for (RuleExpression ruleExpression : ruleExpressionList) {if (!ruleExpression.interpreter(state)) {return false;}}return true;}
}/*** or 表达式* @Date 2022/11/5* @Author lifei*/
public class RuleORExpression implements RuleExpression{private List<RuleExpression> ruleExpressionList = new ArrayList<>();public RuleORExpression(String expression) {String[] subExpressions = expression.trim().split("\\|\\|");for (String subExpression : subExpressions) {ruleExpressionList.add(new RuleAndExpression(subExpression));}}@Overridepublic boolean interpreter(Map<String, Long> state) {for (RuleExpression ruleExpression : ruleExpressionList) {if (ruleExpression.interpreter(state)) {return true;}}return false;}
}

应用表达式:

/*** @Date 2022/11/5* @Author lifei*/
public class AlertRuleInterpreter {private RuleExpression ruleExpression;// key1 > 100 && key2 <1000 || key3 == 200public AlertRuleInterpreter(String ruleExpression) {this.ruleExpression = new RuleORExpression(ruleExpression);}public boolean interpreter(Map<String, Long> state) {return this.ruleExpression.interpreter(state);}
}

3.3 命令模式

命令模式可将“动作的请求者”从“动作的执行者”对象中解耦。比如,命令是遥控器,执行对象是厂商类。

命令模式:将“请求”封装成对象,以便使用不同的请求、队列或日志来参数化其他对象。命令模式也支持可撤销操作。

命令模式的主要作用和应用场景,是用来控制命令的执行,比如,异步、延迟、排队执行命令、撤销重做命令、存储命令、给命令记录日志等等

策略模式和工厂模式的区别:

策略模式包含策略的定义、创建和使用三部分。策略模式侧重“策略”或“算法”这个特定的应用场景,用来解决根据运行时状态从一组策略中选择不同策略的问题。

工厂模式侧重封装对象的创建过程,这里的对象没有任何业务场景的限定,可以是策略,但也可以是其他东西。

命令模式和工厂模式的区别:

在策略模式中,不同的策略具有相同的目的、不同的实现、互相之间可以替换。比如,不同的排序算法可以相互替换。

在命令模式中,不同的命令具有不同的目的,对应不同的处理逻辑,并且互相之间不可替换。

示例一:遥控板上每个按钮都是一个命令

(1)遥控板:
/*** 遥控板上有多个按钮,每个按钮都对应一个命令* @Date 2022/11/5* @Author lifei*/
public class RemoteControl {// 启动按钮private Command[] onCommands;// 关闭按钮private Command[] offCommands;public RemoteControl() {int num = 7;// 定义两排按钮,七个启动开关的按钮,七个关闭的按钮this.onCommands = new Command[num];this.offCommands = new Command[num];// 对两排安妮进行初始化,初始化一个不做任何功能的NoCommand noCommand = new NoCommand();for (int i = 0; i < num; i++) {this.onCommands[i] = noCommand;this.offCommands[i] = noCommand;}}/*** 设置按钮功能* @param slot* @param onCommand* @param offCommand*/public void setCommand(int slot, Command onCommand, Command offCommand) {this.onCommands[slot] = onCommand;this.offCommands[slot] = offCommand;}/*** 开启按钮是被按下* @param slot*/public void onButtonWasPushed(int slot) {this.onCommands[slot].execute();}/*** 关闭按钮是被按下* @param slot*/public void offButtonWasPushed(int slot) {this.offCommands[slot].execute();}/*** 打印遥控器功能* @return*/@Overridepublic String toString() {MoreObjects.ToStringHelper toStringHelper = MoreObjects.toStringHelper(RemoteControl.class);for (int i = 0; i < onCommands.length; i++) {toStringHelper.add("[slot " + i + "] " + onCommands[i].getClass().getName(), offCommands[i].getClass().getName());}return toStringHelper.toString();}
}
(2)实现命令
public interface Command {void execute();
}/*** 电灯,有开和关功能* @Date 2022/11/5* @Author lifei*/
public class Light {private String name;public Light(String name) {this.name = name;}public void on() {System.out.println(name + " 电灯打开");}public void off() {System.out.println(name + " 电灯关闭");}
}/*** 关闭开关的命令* @Date 2022/11/5* @Author lifei*/
public class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {this.light.off();}
}/*** 电灯打开的命令* @Date 2022/11/5* @Author lifei*/
public class LightOnCommand implements Command {private Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.on();}
}/*** 音响* @Date 2022/11/5* @Author lifei*/
public class Stereo {private String name;public Stereo(String name) {this.name = name;}// 音量private int volume;public void on() {System.out.println(name + " 打开音响...");}public void setCD() {System.out.println(name + " 选择CD");}public void setVolume(int volume) {this.volume = volume;System.out.println(name + " 将音量设置为: " + volume);}public void off() {System.out.println("关闭音响");}
}/*** @Date 2022/11/5* @Author lifei*/
public class StereoOffCommand implements Command {private Stereo stereo;public StereoOffCommand(Stereo stereo) {this.stereo = stereo;}@Overridepublic void execute() {this.stereo.off();}
}/*** 音响打开的命令* @Date 2022/11/5* @Author lifei*/
public class StereoOnWithCDCommand implements Command {private Stereo stereo;public StereoOnWithCDCommand(Stereo stereo) {this.stereo = stereo;}@Overridepublic void execute() {this.stereo.on();this.stereo.setCD();this.stereo.setVolume(11);}
}/*** 不做任何功能的按钮*  很多时候,空对象本身也被视为一种设计模式* @Date 2022/11/5* @Author lifei*/
public class NoCommand implements Command {@Overridepublic void execute() {}
}
(3)测试遥控器
/*** @Date 2022/11/5* @Author lifei*/
public class ApplicationDemo {public static void main(String[] args) {RemoteControl remoteControl = new RemoteControl();Light livingRoomLight = new Light("客厅的电灯");Stereo stereo = new Stereo("大成音响");// 电灯命令LightOnCommand lightOnCommand = new LightOnCommand(livingRoomLight);LightOffCommand lightOffCommand = new LightOffCommand(livingRoomLight);// 音响命令StereoOnWithCDCommand stereoOnWithCDCommand = new StereoOnWithCDCommand(stereo);StereoOffCommand stereoOffCommand = new StereoOffCommand(stereo);// 设置命令remoteControl.setCommand(0, lightOnCommand, lightOffCommand);remoteControl.setCommand(1, stereoOnWithCDCommand, stereoOffCommand);// 打印遥控板System.out.println(remoteControl);// 按下按钮remoteControl.onButtonWasPushed(0);remoteControl.offButtonWasPushed(0);remoteControl.onButtonWasPushed(1);remoteControl.offButtonWasPushed(1);}
}

示例二:添加撤销功能

(1)改造命令接口,添加一个撤销功能
public interface Command {void execute();// 添加一个撤销功能default void undo(){}
}
(2)所有的命令实现都要添加一个撤销功能
/*** 关闭开关的命令* @Date 2022/11/5* @Author lifei*/
public class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {this.light.off();}@Overridepublic void undo() {this.light.on();}
}
(3)遥控器上添加一个撤销按钮
public class RemoteControl {// 启动按钮private Command[] onCommands;// 关闭按钮private Command[] offCommands;// 为遥控起添加一个撤销按钮private Command undoCommand;public RemoteControl() {int num = 7;// 定义两排按钮,七个启动开关的按钮,七个关闭的按钮this.onCommands = new Command[num];this.offCommands = new Command[num];// 对两排安妮进行初始化,初始化一个不做任何功能的NoCommand noCommand = new NoCommand();for (int i = 0; i < num; i++) {this.onCommands[i] = noCommand;this.offCommands[i] = noCommand;}// 撤销按钮初始化this.undoCommand = noCommand;}/*** 设置按钮功能* @param slot* @param onCommand* @param offCommand*/public void setCommand(int slot, Command onCommand, Command offCommand) {this.onCommands[slot] = onCommand;this.offCommands[slot] = offCommand;}/*** 开启按钮是被按下* @param slot*/public void onButtonWasPushed(int slot) {this.onCommands[slot].execute();this.undoCommand = this.onCommands[slot];}/*** 关闭按钮是被按下* @param slot*/public void offButtonWasPushed(int slot) {this.offCommands[slot].execute();this.undoCommand = this.offCommands[slot];}/*** 按下撤销按钮*/public void undoButtonWasPushed() {this.undoCommand.undo();}
}
(4)测试遥控器
/*** @Date 2022/11/5* @Author lifei*/
public class ApplicationDemo {public static void main(String[] args) {RemoteControl remoteControl = new RemoteControl();Light livingRoomLight = new Light("客厅的电灯");Stereo stereo = new Stereo("大成音响");// 电灯命令LightOnCommand lightOnCommand = new LightOnCommand(livingRoomLight);LightOffCommand lightOffCommand = new LightOffCommand(livingRoomLight);// 音响命令StereoOnWithCDCommand stereoOnWithCDCommand = new StereoOnWithCDCommand(stereo);StereoOffCommand stereoOffCommand = new StereoOffCommand(stereo);// 设置命令remoteControl.setCommand(0, lightOnCommand, lightOffCommand);remoteControl.setCommand(1, stereoOnWithCDCommand, stereoOffCommand);// 打印遥控板System.out.println(remoteControl);// 按下按钮remoteControl.onButtonWasPushed(0);remoteControl.offButtonWasPushed(0);// 测试撤销按钮remoteControl.undoButtonWasPushed();remoteControl.onButtonWasPushed(1);remoteControl.offButtonWasPushed(1);// 测试撤销按钮remoteControl.undoButtonWasPushed();}
}

示例三:实现一个需要记录状态的撤销功能

/*** 电扇命令* @Date 2022/11/5* @Author lifei*/
public class CeilingFanHighCommand implements Command {private CeilingFan ceilingFan;private int prevSpeed;public CeilingFanHighCommand(CeilingFan ceilingFan) {this.ceilingFan = ceilingFan;}@Overridepublic void execute() {// 记录当前的速度,以便撤销的时候使用this.prevSpeed = ceilingFan.getSpeed();ceilingFan.high();System.out.println("设置成高档位");}@Overridepublic void undo() {if (prevSpeed == CeilingFan.HIGH) {ceilingFan.high();} else if (prevSpeed == CeilingFan.MEDIUM) {ceilingFan.medium();} else if (prevSpeed == CeilingFan.LOW) {ceilingFan.low();} else if (prevSpeed == CeilingFan.OFF) {ceilingFan.off();}}
}

3.4 迭代器模式

迭代器模式(也叫游标模式):提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

把游走的任务放在迭代器上,而不是聚合上。这样简化了聚合的接口和实现,也让责任各得各得其所。

迭代器的实现类,通常以内部类的形式出现。

(1)迭代器模式的示例

不同的餐馆(晚餐菜单、煎饼屋菜单、咖啡馆菜单),通过实现java.util.Iterable接口中的iterator()方法,返回一个迭代器Iterator。通过迭代器,进行统一风格的菜单项遍历。Java5提供了for/in语法糖,可以直接对Iterable或数组进行 遍历。

/*** 煎饼屋的菜单* @Date 2022/10/29* @Author lifei*/
public class PancakeHouseMenu implements Iterable<MeanItem> {private ArrayList<MeanItem> meanItems;public PancakeHouseMenu() {meanItems = new ArrayList<>();addItem("Pancake01", "001",  false, 2.99);addItem("Pancake02", "002",  true, 3.99);addItem("Pancake03", "003",  false, 1.99);addItem("Pancake04", "004",  true, 5.99);}public void addItem(String name, String description, boolean vegetarian, double price) {MeanItem meanItem = new MeanItem(name, description, vegetarian, price);meanItems.add(meanItem);}@Overridepublic Iterator<MeanItem> iterator() {return new PancakeHouseMenuIterator(meanItems);}
}/*** 晚餐菜单* 为了便利菜单,要返回一个迭代器,Iterable 里面定义了返回迭代器的方法* @Date 2022/10/29* @Author lifei*/
public class DinerMenu implements Iterable<MeanItem>{private static final int MAX_ITEMS = 6;private int numberOfItems = 0;private MeanItem[] meanItems;public DinerMenu() {this.meanItems = new MeanItem[MAX_ITEMS];addItem("Diner01", "001",  false, 2.99);addItem("Diner02", "002",  true, 3.99);addItem("Diner03", "003",  false, 1.99);addItem("Diner04", "004",  true, 5.99);}public void addItem(String name, String description, boolean vegetarian, double price) {MeanItem meanItem = new MeanItem(name, description, vegetarian, price);meanItems[numberOfItems++] = meanItem;}@Overridepublic Iterator<MeanItem> iterator() {return new DinerMenuIterator(meanItems);}
}/*** 咖啡菜单* @Date 2022/10/29* @Author lifei*/
public class CafeMenu implements Iterable<MeanItem> {private Map<String, MeanItem> meanItems = new HashMap<>();public CafeMenu() {addItem("Cafe01", "001", true, 5.31);addItem("Cafe02", "002", false, 1.31);addItem("Cafe03", "003", true, 2.31);addItem("Cafe04", "004", false, 3.31);}public void addItem(String name, String description, boolean vegetarian, double price) {MeanItem meanItem = new MeanItem(name, description, vegetarian, price);meanItems.put(name, meanItem);}@Overridepublic Iterator<MeanItem> iterator() {return meanItems.values().iterator();}
}

菜单项:

/*** 菜单项* @Date 2022/10/29* @Author lifei*/
public class MeanItem {private final String name;private final String description;private final boolean vegetarian;private final double price;public MeanItem(String name, String description, boolean vegetarian, double price) {this.name = name;this.description = description;this.vegetarian = vegetarian;this.price = price;}public String getName() {return name;}public String getDescription() {return description;}public boolean isVegetarian() {return vegetarian;}public double getPrice() {return price;}@Overridepublic String toString() {return MoreObjects.toStringHelper(MeanItem.class).add("name", name).add("description", description).add("vegetarian", vegetarian).add("price", price).toString();}
}

服务员遍历菜单:

/*** @Date 2022/10/29* @Author lifei*/
public class Waitress {//    private DinerMenu dinerMenu;private Iterable<MeanItem> dinerMenu;
//    private PancakeHouseMenu pancakeHouseMenu;private Iterable<MeanItem> pancakeHouseMenu;private Iterable<MeanItem> cafeMenu;public Waitress(Iterable<MeanItem> dinerMenu, Iterable<MeanItem> pancakeHouseMenu,Iterable<MeanItem> cafeMenu) {this.dinerMenu = dinerMenu;this.pancakeHouseMenu = pancakeHouseMenu;this.cafeMenu = cafeMenu;}public void printMenu() {// 煎饼屋菜单迭代器Iterator<MeanItem> pancakeIterator = pancakeHouseMenu.iterator();// 晚餐菜单迭代器Iterator<MeanItem> dinerIterator = dinerMenu.iterator();// 咖啡菜单Iterator<MeanItem> cafeIterator = cafeMenu.iterator();System.out.println("煎饼屋菜单......:");printMean(pancakeIterator);System.out.println("晚餐菜单......:");printMean(dinerIterator);System.out.println("咖啡菜单......");printMean(cafeIterator);}private void printMean(Iterator iterator) {while (iterator.hasNext()) {System.out.println(iterator.next());}// Java5 中包含了 新形式的for语句,称为for/in,底层基于迭代器实现。// 可以让你在一个集合或者一个数组中遍历,而且不需要显式的创建迭代器System.out.println("使用Java5的for/in 语法糖,遍历 cafeMean:");for (MeanItem menu : cafeMenu) {System.out.println(menu);}}
}

运行迭代器示例:

/*** 迭代器模式* @Date 2022/10/29* @Author lifei*/
public class ApplicationDemo {public static void main(String[] args) {Iterable<MeanItem> dinerMenu = new DinerMenu();Iterable<MeanItem> pancakeHouseMenu = new PancakeHouseMenu();Iterable<MeanItem> cafeMean = new CafeMenu();Waitress waitress = new Waitress(dinerMenu, pancakeHouseMenu, cafeMean);waitress.printMenu();}
}

(2)迭代器Iterator接口两种常见的定义

// 接口定义方式一
public interface Iterator<E> {boolean hasNext();void next();E currentItem();
}// 接口定义方式二
public interface Iterator<E> {boolean hasNext();E next();
}

(3)ArrayList使用迭代器在遍历的时候,调用remove()方法会不会报错

ArrayList使用迭代器在遍历的时候,调用remove方法会不会报错。

(4)支持快照的迭代器

支持快照:迭代器被创建出来后,对list进行添加和删除操作,不影响已经生成的迭代器。

思路一:创建迭代器的时候,拷贝一份数据;

思路二:用快照时间,和创建时间,删除时间做对比;

下面代码实现第二种思路:

public interface List<E>  extends Iterable<E> {void add(E obj);void remove(E obj);
}/*** 包含支持快照的迭代器:*  每个元素都有一个时间: addTime 和 delTime*     添加一个元素的时候: addTime 为当前时间, delTime 为 Long.MAX_VALUE*     删除一个元素的时候: delTime 修改为当前时间*     迭代遍历的时候,取: addTime< currentTime < delTime 的元素* @Date 2022/10/29* @Author lifei*/
public class ArrayList<E> implements List<E>{private static final int DEFAULT_CAPACITY = 10;private int actualSize; // 不包含删除标记元素private int totalSize; //  包含删除标记元素private E[] elements;private long[] addTimestamps;private long[] delTimestamps;public ArrayList() {this.elements = (E[])new Object[DEFAULT_CAPACITY];this.addTimestamps = new long[DEFAULT_CAPACITY];this.delTimestamps = new long[DEFAULT_CAPACITY];this.actualSize = 0;this.totalSize = 0;}@Overridepublic void add(E obj) {elements[totalSize] = obj;addTimestamps[totalSize] = System.nanoTime();delTimestamps[totalSize] = Long.MAX_VALUE;totalSize++;actualSize++;}@Overridepublic void remove(E obj) {for (int i = 0; i < totalSize; i++) {if (Objects.equals(elements[i], obj)) {delTimestamps[i] = System.nanoTime();actualSize--;}}}public int actualSize() {return this.actualSize;}public int totalSize() {return this.totalSize;}public E get(int i) {if (i>=totalSize) {throw new IndexOutOfBoundsException();}return elements[i];}public long getAddTimestamp(int i) {if (i >= totalSize) {throw new IndexOutOfBoundsException();}return addTimestamps[i];}public long getDelTimestamp(int i) {if (i>=totalSize) {throw new IndexOutOfBoundsException();}return delTimestamps[i];}@Overridepublic Iterator<E> iterator() {return new SnapshotArrayIterator(this);}
}/*** @Date 2022/10/30* @Author lifei*/
public class SnapshotArrayIterator<E> implements Iterator<E> {private long snapshotTimestamp;private int cursorInAll; // 在整个容器的下标,而非快照的下标private int leftCount; // 快照中还有几个元素未被遍历private ArrayList<E> list;public SnapshotArrayIterator(ArrayList<E> list) {this.snapshotTimestamp = System.nanoTime();this.list = list;this.leftCount = list.actualSize();this.cursorInAll = 0;justNext();}@Overridepublic boolean hasNext() {return this.leftCount>0;}@Overridepublic E next() {E currentItem = list.get(cursorInAll++);justNext();leftCount--;return currentItem;}private void justNext() {if (leftCount>0) {while (cursorInAll<list.totalSize()) {if (snapshotTimestamp>list.getAddTimestamp(cursorInAll)&& snapshotTimestamp< list.getDelTimestamp(cursorInAll)) {break;}cursorInAll++;}}}
}

测试:

/*** 测试支持快照的迭代器* @Date 2022/10/30* @Author lifei*/
public class ApplicationDemo {public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();list.add(2);list.add(3);list.add(4);list.add(5);Iterator<Integer> iterator1 = list.iterator();list.remove(2);Iterator<Integer> iterator2 = list.iterator();showIterator(iterator1);showIterator(iterator2);}private static void showIterator(Iterator<Integer> iterator) {while (iterator.hasNext()) {System.out.print(iterator.next() + ", ");}System.out.println();}
}

3.5 中介者模式

中介者模式:中介模式定义了一个单独的(中介)对象,来封装一组对象之间的交互。将这组对象之间的交互委派给与中介对象交互,来避免对象之间的直接交互。

中介模式的设计思想跟中间层很像,通过引入中介这个中间层,将一组对象之间的交互关系(或者说依赖关系)从多对多(网状关系)转换为一对多(星状关系)。

在使用中介模式的时候,我们要根据实际的情况,平衡对象之间交互的复杂度和中介类本身的复杂度。

中介者模式和观察者模式的区别:

在观察者模式中,交互关系往往都是单向的,一个参与者要么是观察者,要么是被观察者,不会兼具两种身份。

中介模式中,交互关系错综复杂,除此之外,还能进行顺序控制。

如果一个参与者状态的改变,其他参与者执行的操作有一定先后顺序的要求,这个时候,中介模式就可以利用中介类,通过先后调用不同参与者的方法,来实现顺序的控制,而观察者模式是无法实现这样的顺序要求的。

3.6 观察者模式(发布订阅模式)

观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

(1)观察者模式(自己实现)

主题抽象:三个经典方法

/*** 主题: 注册观察者、移除观察者,通知观察者* @Date 2022/10/21* @Author lifei*/
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);// 当主题状态改变的时候,这个方法会被调用,以通知所有的观察者void notifyObservers();
}

观察者抽象:有一个更新状态的方法

/*** 观察者:当主题状态发生变化的时候,依赖该主题的观察者会自动更新* @Date 2022/10/21* @Author lifei*/
public interface Observer {void update(float temp, float humidity, float pressure);
}

观察者的公共接口:

/***  观察者自己的行为方法* @Date 2022/10/21* @Author lifei*/
public interface DisplayElement {void display();
}

主题的实现:

/*** 一个天气数据主题* @Date 2022/10/22* @Author lifei*/
public class WeatherDataSubject implements Subject {private List<Observer> observerList = new ArrayList<>();private float temperature;private float humidity;private float pressure;@Overridepublic void registerObserver(Observer observer) {observerList.add(observer);}@Overridepublic void removeObserver(Observer observer) {int i = observerList.indexOf(observer);if (i>=0) {observerList.remove(i);}}@Overridepublic void notifyObservers() {for (Observer observer : observerList) {observer.update(temperature, humidity, pressure);}}/*** 改变主题的状态,并通知观察者* @param temperature* @param humidity* @param pressure*/public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}/*** 通知观察者*/private void measurementsChanged() {notifyObservers();}
}

一个观察者实现:

/*** 一个观察者实现: 通过构造函数把自己注册进一个主题* @Date 2022/10/22* @Author lifei*/
public class CurrentConditionsDisplayObserver implements Observer, DisplayElement {private Subject weatherData;private float temperature;private float humidity;// 通过构造函数,把自己注册进一个主题public CurrentConditionsDisplayObserver(Subject subject) {this.weatherData = subject;subject.registerObserver(this);}@Overridepublic void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}@Overridepublic void update(float temp, float humidity, float pressure) {this.temperature = temp;this.humidity = humidity;display();}
}

(2)JDK自带的观察者模式

可观察者:

/*** 可观察者* 继承 java.util.Observable* @Date 2022/10/22* @Author lifei*/
public class WeatherDataObservable extends Observable {private float temperature;private float humidity;private float pressure;public void setMeasureMents(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}/*** 调用 notifyObservers() 之前,要先调用setChanged() 来指示状态已经改变* notifyObservers() 有一个重载方法 notifyObservers(args)*/public void measurementsChanged() {// setChanged() 方法用来标记状态已经改变的事实,好让notifyObservers() 知道当它被调用时应该更新观察者。// 如果调用notifyObservers() 之前没有先调用 setChanged(), 观察者就不会被通知setChanged();// 不传递参数,观察者就需要从可观察者对象中"拉"(pull)数据notifyObservers();//如果传递参数,就是推送数据给观察者
//        Object args = new float[]{temperature, humidity, pressure};
//        notifyObservers(args);}// 观察者会利用下面三个方法获取WeatherData对象的状态public float getTemperature() {return temperature;}public float getHumidity() {return humidity;}public float getPressure() {return pressure;}
}

观察者:

下面是观察者,从可观察者拉取数据的例子。

注意:

  • 可观察者是一个类,这限制了
/*** 观察者* 实现java.util.Observer* @Date 2022/10/22* @Author lifei*/
public class CurrentConditionsDisplay implements Observer, DisplayElement {private Observable observable;private float temperature;private float humidity;public CurrentConditionsDisplay(Observable observable) {this.observable = observable;observable.addObserver(this);}@Overridepublic void update(Observable o, Object arg) {if (o instanceof WeatherDataObservable) {WeatherDataObservable weatherDataObservable = (WeatherDataObservable) o;this.temperature = weatherDataObservable.getTemperature();this.humidity = weatherDataObservable.getHumidity();display();}}@Overridepublic void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}
}
public class WeatherStation {public static void main(String[] args) {// 可观察者WeatherDataObservable weatherDataObservable = new WeatherDataObservable();// 观者者java.util.Observer observer1 = new CurrentConditionsDisplay(weatherDataObservable);// 改变可观察者的状态weatherDataObservable.setMeasureMents(20.1f, 0.3f, 105.1f);}
}

(3)Guava的EventBus

动手实现一个EventBus。

当调用Eventbus.post(obj)发送数据的时候,和接收消息的类型是obj类型的父类型的观察者方法会被触发。

当我们调用 post() 函数发送消息的时候,并非把消息发送给所有的观察者,而是发送给可匹配的观察者。所谓可匹配指的是,能接收的消息类型是发送消息(post 函数定义中的 event)类型的父类

AObserver 能接收的消息类型是 XMsg,BObserver 能接收的消息类型是 YMsg,CObserver 能接收的消息类型是 ZMsg。其中,XMsg 是 YMsg 的父类。当我们如下发送消息的时候,相应能接收到消息的可匹配观察者如下所示:


XMsg xMsg = new XMsg();
YMsg yMsg = new YMsg();
ZMsg zMsg = new ZMsg();
post(xMsg); => AObserver接收到消息
post(yMsg); => AObserver、BObserver接收到消息
post(zMsg); => CObserver接收到消息

创建一个主题:

/*** 一个主题* @Date 2022/10/22* @Author lifei*/
public class WeatherDataEvent {private float temperature;private float humidity;private float pressure;public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;}public float getTemperature() {return temperature;}public float getHumidity() {return humidity;}public float getPressure() {return pressure;}
}

创建一个观察者:

/*** 一个观察者:使用了Guava的@Subscribe 注解* @Date 2022/10/22* @Author lifei*/
public class CurrentConditionsDisplayListener{/*** 添加一个订阅主题的竹梅*/@Subscribepublic void display(WeatherDataEvent weatherDataEvent) {System.out.println("Current conditions: " + weatherDataEvent.getTemperature() + "F degrees and " + weatherDataEvent.getHumidity() + "% humidity");}
}

使用通过EventBus串通:

public class WeatherStation {public static void main(String[] args) {// 定义一个EventBus
//        EventBus eventBus = new EventBus(); // 同步阻塞模式final int DEFAULT_EVENTBUS_THREAD_POOL_SIZE = 20; // 异步非阻塞线程池大小// 异步非阻塞的方式// 异步非阻塞的方式ExecutorService executorService = Executors.newFixedThreadPool(DEFAULT_EVENTBUS_THREAD_POOL_SIZE);EventBus eventBus = new AsyncEventBus(executorService);// 注册一个观察者eventBus.register(new CurrentConditionsDisplayListener());// 创建一个主题WeatherDataEvent weatherDataEvent = new WeatherDataEvent();weatherDataEvent.setMeasurements(20.1f, 0.3f, 105.1f);// 发布主题(会通知观察者), post里传递的是观察者接收的数据,eventBus.post(weatherDataEvent);// 关闭线程池executorService.shutdown();}
}

@Subscribe注解所在的方法参数,可以是任意Object类型(不能是基本数据类型)。

public class CurrentConditionsDisplayListener{/*** 添加一个订阅主题的竹梅*/@Subscribepublic void display(WeatherDataEvent weatherDataEvent) {System.out.println("Current conditions: " + weatherDataEvent.getTemperature() + "F degrees and "+ weatherDataEvent.getHumidity() + "% humidity");}@Subscribepublic void display(Float temperature) {System.out.println("Current conditions: " + temperature + "F degrees and "+ 0.3 + "% humidity");}
}public class WeatherStation {public static void main(String[] args) {// 定义一个EventBus
//        EventBus eventBus = new EventBus(); // 同步阻塞模式final int DEFAULT_EVENTBUS_THREAD_POOL_SIZE = 20; // 异步非阻塞线程池大小// 异步非阻塞的方式ExecutorService executorService = Executors.newFixedThreadPool(DEFAULT_EVENTBUS_THREAD_POOL_SIZE);EventBus eventBus = new AsyncEventBus(executorService);// 注册一个观察者eventBus.register(new CurrentConditionsDisplayListener());// 创建一个主题WeatherDataEvent weatherDataEvent = new WeatherDataEvent();weatherDataEvent.setMeasurements(20.1f, 0.3f, 105.1f);// 发布主题(会通知观察者)
//        eventBus.post(weatherDataEvent);eventBus.post(weatherDataEvent.getTemperature());// 关闭线程池executorService.shutdown();}
}

3.7 备忘录模式

主要用来防丢失、撤销、恢复等。

备忘录模式:在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态.

要点一:存储副本以便后期恢复;要点二:不违背封装原则。

备忘录模式和备份的区别:备忘录模式更侧重于代码的设计和实现,备份更侧重架构设计或产品设计。

大对象的备份(备份占用的存储空间会比较大,备份和恢复的耗时会比较长),常见的处理办法:只备份必要的恢复信息,结合最新的数据来恢复;再比如,全量备份和增量备份相结合,低频全量备份,高频增量备份,两者结合来做恢复。

示例一:撤销上一次输入

(1)版本一:违背了封装原则
/*** 当前的文本内容* @Date 2022/11/1* @Author lifei*/
public class InputText {private StringBuilder text = new StringBuilder();public String getText() {return text.toString();}public void append(String input) {text.append(input);}public void setText(String text) {this.text.replace(0, this.text.length(), text);}
}

之前的备份:

/*** 历史内容备份* @Date 2022/11/1* @Author lifei*/
public class SnapshotHolder {private Stack<InputText> snapshots = new Stack<>();public InputText popSnapShot() {return snapshots.pop();}public void pushSnapshot(InputText inputText) {InputText deepClonedInputText = new InputText();deepClonedInputText.setText(inputText.getText());snapshots.push(deepClonedInputText);}
}

应用:

/*** 应用* @Date 2022/11/1* @Author lifei*/
public class ApplicationMain {public static void main(String[] args) {InputText inputText = new InputText();SnapshotHolder snapshotHolder = new SnapshotHolder();Scanner scanner = new Scanner(System.in);while (scanner.hasNext()) {String input = scanner.next();if (StringUtils.equalsIgnoreCase(input, ":list")) {System.out.println(inputText.getText());}else if (StringUtils.equalsIgnoreCase(input, ":undo")) {InputText snapShot = snapshotHolder.popSnapShot();inputText.setText(snapShot.getText());}else {snapshotHolder.pushSnapshot(inputText);inputText.append(input);}}}
}

上面的存在的问题:

  • InputText的set() 方法可能被其它地方利用,违背封装原则;
  • 快照理论上不应该被修改;
(2)版本二:使用备忘录模式进行改造

一个单独的快照类

/*** 一个快照类* @Date 2022/11/2* @Author lifei*/
public class Snapshot {private final String text;public Snapshot(String text) {this.text = text;}public String getText() {return text;}
}

通过InputText创建快照,或者根据快照更新内容:

/*** 输入的内容* @Date 2022/11/2* @Author lifei*/
public class InputText {private StringBuilder text = new StringBuilder();public void append(String input) {text.append(input);}public String getText() {return text.toString();}public Snapshot createSnapshot() {return new Snapshot(text.toString());}public void restoreSnapshot(Snapshot snapshot) {text.replace(0, text.length(), snapshot.getText());}
}

快照持有者:

/*** 快照的持有者* @Date 2022/11/2* @Author lifei*/
public class SnapshotHolder {private Stack<Snapshot> snapshots = new Stack<>();public Snapshot popSnapshot() {return snapshots.pop();}public void pushSnapshot(Snapshot snapshot) {this.snapshots.push(snapshot);}
}

应用:

/*** 应用* @Date 2022/11/2* @Author lifei*/
public class ApplicationMain {public static void main(String[] args) {SnapshotHolder snapshotHolder = new SnapshotHolder();InputText inputText = new InputText();Scanner scanner = new Scanner(System.in);while (scanner.hasNext()) {String input = scanner.next();if (StringUtils.equalsIgnoreCase(input, ":list")) {System.out.println(inputText.getText());}else if (StringUtils.equalsIgnoreCase(input, ":undo")) {inputText.restoreSnapshot(snapshotHolder.popSnapshot());}else {snapshotHolder.pushSnapshot(inputText.createSnapshot());inputText.append(input);}}}
}

3.8 模版方法模式

模版方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模版方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

这里的算法,可以理解为广义的“业务逻辑”。算法骨架就是模版,包含算法骨架的方法就是“模版方法”。

模版方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。

模版方法模式是为了解决复用和扩展两个问题。

模版方法模式的应用:

  • 复用:Java InputStream、Java AbstractList
  • 扩展:Java HttpServlet的service() 方法就是一个模版方法、TestCase

模版方法模式和回调函数:

  • 从应用场景来看:同步回调跟模版方法模式几乎一样。异步回调跟模版方法模式有较大差别,更像是观察者模式;
  • 从代码实现上看:回调基于组合关系来实现。模版方法模式基于继承来实现;

组合优于继承。

(1)示例一:一个带有钩子的模版:

/*** 定义一个带有钩子的模版方法* @Date 2022/10/23* @Author lifei*/
public abstract class CaffeineBeverageWithHook {/*** 模版方法*/public void prepareRecipe() {boilWater(); // 烧水brew(); // 冲泡pourInCup(); // 倒入杯中// 由钩子方法决定是否添加配料if (customerWantsCondiments()) {addCondiments(); // 添加配料}}public abstract void brew();public abstract void addCondiments();public void boilWater() {System.out.println("烧水.....");}public void pourInCup() {System.out.println("倒入被中......");}/*** 钩子方法,由子类决定是否覆盖* @return*/public boolean customerWantsCondiments() {return true;}
}

实现:

/*** 模版方法模式:子类* @Date 2022/10/23* @Author lifei*/
public class CoffeeWithHook extends CaffeineBeverageWithHook {@Overridepublic void brew() {out.println("冲泡咖啡");}@Overridepublic void addCondiments() {out.println("添加 牛奶");}/*** 实现钩子方法* @return*/@Overridepublic boolean customerWantsCondiments() {String answer = getUserInput();if (StringUtils.startsWithIgnoreCase(answer, "y")) {return true;}else {return false;}}/*** 获取用户输入* @return*/private String getUserInput() {String answer = null;out.println("你想添加牛奶配料吗?");try (InputStreamReader inputStreamReader = new InputStreamReader(in);BufferedReader reader = new BufferedReader(inputStreamReader)) {answer = reader.readLine();} catch (IOException e) {throw new RuntimeException(e);}if (answer==null) {return "no";}else {return answer;}}
}
/***  模版方法,示例* @Date 2022/10/23* @Author lifei*/
public class TemplateMain {public static void main(String[] args) {CaffeineBeverageWithHook coffee = new CoffeeWithHook();coffee.prepareRecipe();}
}

3.9 状态模式

状态模式:允许对象(context)在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

context(上下文)是一个类,他可以拥有一些内部状态,第一个示例中,GumballMachine 就是context。在状态模式中,context的行为随时委托到那些状态对象中的一个。随着时间的流逝,当前状态在状态对象集合中游走改变,以反映出context内部的状态,因此,context的行为也会跟着改变。但是context的客户对于状态对象了解不多,甚至根本是浑然不觉。

状态模式是状态机的一种实现方式。状态机又叫有限状态机,它有3部分组成:状态、事件、动作。其中,事件被称为转移条件。

状态模式,将事件触发的状态转移和动作执行,拆分到不同的状态类中。

(1)第一个示例

版本一:没有使用状态
/*** 糖果售货机* @Date 2022/10/25* @Author lifei*/
public class GumballMachine {// 售罄private final static int SOLD_OUT = 0;// 没有25分钱private final static int NO_QUARTER = 1;//有25分private final static int HAS_QUARTER = 2;// 售出糖果private final static int SOLD = 3;private int state = SOLD_OUT;private int count;public GumballMachine(int count) {this.count = count;if (count>0) {state = NO_QUARTER;}}/*** 投入硬币*/public void insertQuarter() {if (state == HAS_QUARTER) { //已经有硬币了System.out.println("你不用投入另一个硬币");} else if (state == SOLD_OUT) { // 售罄System.out.println("你不能投入硬币,因为这个机器的糖果销售完了");} else if (state == SOLD) {System.out.println("请等待,我们将给你一个糖果");} else if (state == NO_QUARTER) {state = HAS_QUARTER;System.out.println("你投入了一个硬币");}}/*** 退回硬币*/public void ejectQuarter() {if (state == HAS_QUARTER) {System.out.println("硬币退回");state = NO_QUARTER;} else if (state == NO_QUARTER) {System.out.println("你还没有投入硬币");} else if (state == SOLD) {System.out.println("糖果已经销售给你了");} else if (state == SOLD_OUT) {System.out.println("不能退,因为你没有投入硬币");}}/*** 转动曲柄*/public void turnCrank() {if (state == SOLD) {System.out.println("转动两次,不能给你另一个糖果");} else if (state == NO_QUARTER) {System.out.println("你转动了曲柄,但是你还没有投入硬币");} else if (state == SOLD_OUT) {System.out.println("你转动了曲柄,但是这里没有糖果");} else if (state == HAS_QUARTER) {System.out.println("你转动了......");state = SOLD;dispense();}}/*** 发放糖果*/private void dispense() {if (state == SOLD) {System.out.println("你个糖果是弹出");count = count-1;if (count==0) {System.out.println("售罄了!");state = SOLD_OUT;} else {state = NO_QUARTER;}} else if (state == NO_QUARTER) {System.out.println("你需要先付费!");} else if (state == SOLD_OUT) {System.out.println("没有糖果可以弹出");} else if (state == HAS_QUARTER) {System.out.println("没有糖果售出");}}
}

测试:

/*** 测试状态模式* @Date 2022/10/25* @Author lifei*/
public class GumballMachineTestDrive {public static void main(String[] args) {GumballMachine gumballMachine = new GumballMachine(5);gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.ejectQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.ejectQuarter();gumballMachine.insertQuarter();gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.turnCrank();}
}
版本二:使用状态模式进行改造

定义状态,状态里面带有行为:

/*** 糖果售卖机的状态*/
public interface GumballMachineState {// 投入硬币void insertQuarter();// 退出硬币void ejectQuarter();// 转动手柄void turnCrank();// 售卖糖果void dispense();
}/*** 售罄的状态* @Date 2022/10/25* @Author lifei*/
public class GumballMachineSoldOutState implements GumballMachineState{private GumballMachine gumballMachine;public GumballMachineSoldOutState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("已经售罄,不能投入硬币");}@Overridepublic void ejectQuarter() {System.out.println("已经售罄,没有投入硬币!");}@Overridepublic void turnCrank() {System.out.println("你又转动了转轴,已经售罄了!");}@Overridepublic void dispense() {System.out.println("已经售罄了,不能售出糖果");}
}/*** 没有硬币的状态* @Date 2022/10/25* @Author lifei*/
public class GumballMachineNoQuarterState implements GumballMachineState{private GumballMachine gumballMachine;public GumballMachineNoQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("你投入了一枚硬币.......");gumballMachine.setGumballMachineState(gumballMachine.getHasQuarterState());}@Overridepublic void ejectQuarter() {System.out.println("还没有投入硬币......");}@Overridepublic void turnCrank() {System.out.println("已经转动转轴,但是还没有投入硬币......");}@Overridepublic void dispense() {System.out.println("你需要先投入硬币....");}
}/*** 已经投币的状态* @Date 2022/10/25* @Author lifei*/
public class GumballMachineHasQuarterState implements GumballMachineState{private Random randomWinner = new Random(System.currentTimeMillis());private GumballMachine gumballMachine;public GumballMachineHasQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("你已经投过硬币了......");}@Overridepublic void ejectQuarter() {System.out.println("退出投入的硬币....");this.gumballMachine.setGumballMachineState(gumballMachine.getNoQuarterState());}@Overridepublic void turnCrank() {System.out.println("转动了转轴....");int winner = randomWinner.nextInt(10);if (winner==0 && gumballMachine.getCount()>1) {System.out.println("恭喜,获奖了!");this.gumballMachine.setGumballMachineState(gumballMachine.getWinnerState());} else {this.gumballMachine.setGumballMachineState(gumballMachine.getSoldState());}}@Overridepublic void dispense() {System.out.println("请转动转轴.....");}
}/*** 销售的状态* @Date 2022/10/25* @Author lifei*/
public class GumballMachineSoldState implements GumballMachineState{private GumballMachine gumballMachine;public GumballMachineSoldState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("硬币已经投过了,不需要再次投入硬币。请等待,我们将给你一个糖果!");}@Overridepublic void ejectQuarter() {System.out.println("不能退出硬币,应为你已经转动了转轴!");}@Overridepublic void turnCrank() {System.out.println("你又转动了转轴,但是不会产生更多的糖果......");}@Overridepublic void dispense() {gumballMachine.releaseBall();if (gumballMachine.getCount()==0) {System.out.println("哦,售罄了!");gumballMachine.setGumballMachineState(gumballMachine.getSoldOutState());} else {gumballMachine.setGumballMachineState(gumballMachine.getNoQuarterState());}}
}/*** 售罄的状态* @Date 2022/10/25* @Author lifei*/
public class GumballMachineWinnerState implements GumballMachineState{private GumballMachine gumballMachine;public GumballMachineWinnerState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("你已经中奖了,不需要投入硬币,我们即将给你糖果");}@Overridepublic void ejectQuarter() {System.out.println("你已经中奖了,即将给你糖果,但是不能退出硬币给你。");}@Overridepublic void turnCrank() {System.out.println("你转动了转轴,但是不会给你更多糖果。请稍等,会把奖励的糖果给你。");}@Overridepublic void dispense() {System.out.println("你是获奖者,你能得到两个糖果");gumballMachine.releaseBall();if (gumballMachine.getCount() == 0) {System.out.println("抱歉,售罄了,不能给你第二个糖果");gumballMachine.setGumballMachineState(gumballMachine.getSoldOutState());} else {System.out.println("哇!给你第二个糖果");gumballMachine.releaseBall();if (gumballMachine.getCount()==0) {System.out.println("售罄了!");gumballMachine.setGumballMachineState(gumballMachine.getSoldOutState());} else {gumballMachine.setGumballMachineState(gumballMachine.getNoQuarterState());}}}
}

糖果机器,使用这些状态:

/*** 糖果收获机器* @Date 2022/10/25* @Author lifei*/
public class GumballMachine {// 售罄状态private GumballMachineState soldOutState;// 没有投币状态private GumballMachineState noQuarterState;// 已经投币状体private GumballMachineState hasQuarterState;// 销售状态private GumballMachineState soldState;// 获奖状态private GumballMachineState winnerState;// 初始状态设置为 售罄状态private GumballMachineState gumballMachineState;private int count;public GumballMachine(int count) {this.soldOutState = new GumballMachineSoldOutState(this);this.noQuarterState = new GumballMachineNoQuarterState(this);this.hasQuarterState = new GumballMachineHasQuarterState(this);this.soldState = new GumballMachineSoldState(this);this.winnerState = new GumballMachineWinnerState(this);this.count = count;if (count>0) {gumballMachineState = this.noQuarterState;} else {gumballMachineState = this.soldOutState;}}public void insertQuarter() {gumballMachineState.insertQuarter();}public void ejectQuarter() {gumballMachineState.ejectQuarter();}public void turnCrank() {gumballMachineState.turnCrank();gumballMachineState.dispense();}public GumballMachineState getGumballMachineState() {return gumballMachineState;}public void setGumballMachineState(GumballMachineState gumballMachineState) {this.gumballMachineState = gumballMachineState;}public GumballMachineState getSoldOutState() {return soldOutState;}public GumballMachineState getNoQuarterState() {return noQuarterState;}public GumballMachineState getHasQuarterState() {return hasQuarterState;}public GumballMachineState getSoldState() {return soldState;}public GumballMachineState getWinnerState() {return winnerState;}public int getCount() {return count;}/*** 释放一个糖果*/public void releaseBall() {System.out.println("一个糖果是滚出了售货机!");if (count!=0) {count -= 1;}}
}

测试:

/*** 状态模式测试* @Date 2022/10/25* @Author lifei*/
public class GumballMachineTestDrive {public static void main(String[] args) {GumballMachine gumballMachine = new GumballMachine(5);gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.turnCrank();}
}

(2)第二个示例:超级马里奥游戏

版本一:分支逻辑法

逻辑判断较多,不好维护。

/*** 马里奥的状态* @Date 2022/10/25* @Author lifei*/
public enum MarioState {SMALL(0), // 小马里奥SUPER(1), // 超级马里奥FIRE(2), // 火焰马里奥CAPE(3) // 斗篷马里奥;private int value;MarioState(int value) {this.value = value;}public int getValue() {return value;}
}
/*** 马里奥 状态机: 分支逻辑法* @Date 2022/10/25* @Author lifei*/
public class MarioStateMachine {private int score;private MarioState currentState;public MarioStateMachine() {this.score = 0;this.currentState = SMALL;}/*** 获得蘑菇,变成超级 super, 并加100积分*/public void obtainMushRoom() {if (Objects.equals(currentState,SMALL)) {this.score += 100;this.currentState = SUPER;}}/*** 获得斗篷,加200积分,变身 斗篷马里奥*/public void obtainCape() {if (Objects.equals(currentState,SMALL) || Objects.equals(currentState , SUPER)) {this.currentState = CAPE;this.score += 200;}}/*** 获得火焰,+300分, 变成火焰马里奥*/public void obtainFireFlower() {if (Objects.equals(currentState,SMALL) || Objects.equals(currentState,SUPER)) {this.currentState = FIRE;this.score += 300;}}/*** 遇到怪兽*/public void meetMonster() {if (Objects.equals(currentState,CAPE)) {this.score -= 200;this.currentState = SMALL;} else if (Objects.equals(currentState,SUPER)) {this.score -= 100;this.currentState = SMALL;} else if (Objects.equals(currentState , FIRE)) {this.score -= 300;this.currentState = SMALL;}}public int getScore() {return score;}public MarioState getCurrentState() {return currentState;}
}
版本二:查表法

使用“状态*行为”形成的二维表表示状态的转移。

当操作比较简单的时候,可以使用查表法。

/*** 定义行为* @Date 2022/10/26* @Author lifei*/
public enum MarioEvent {GOT_MUSHROOM(0),GOT_CAPE(1),GOT_FIRE(2),MET_MONSTER(3),;private int value;MarioEvent(int value) {this.value = value;}public int getValue() {return value;}
}
/*** 马里奥 状态机: 查表法* @Date 2022/10/25* @Author lifei*/
public class MarioStateMachine02 {private int score;private MarioState currentState;// 行号是 MarioState.value, 列号是 MarioEvent.valueprivate static final MarioState[][] transitionTable = {// SMALL: GOT_MUSHROOM ,GOT_CAPE ,GOT_FIRE ,MET_MONSTER{SUPER, CAPE, FIRE, SMALL},// SUPER: GOT_MUSHROOM ,GOT_CAPE ,GOT_FIRE ,MET_MONSTER{SUPER, CAPE, FIRE, SMALL},// FIRE: GOT_MUSHROOM ,GOT_CAPE ,GOT_FIRE ,MET_MONSTER{FIRE, FIRE, FIRE, SMALL},// CAPE: GOT_MUSHROOM ,GOT_CAPE ,GOT_FIRE ,MET_MONSTER{CAPE, CAPE, CAPE, SMALL},};private static final int[][] actionTable = {// SMALL: GOT_MUSHROOM ,GOT_CAPE ,GOT_FIRE ,MET_MONSTER{+100, +200, +300, +0},// SUPER: GOT_MUSHROOM ,GOT_CAPE ,GOT_FIRE ,MET_MONSTER{+0, +200, +300, -100},// FIRE: GOT_MUSHROOM ,GOT_CAPE ,GOT_FIRE ,MET_MONSTER{+0, +0, +0, -300},// CAPE: GOT_MUSHROOM ,GOT_CAPE ,GOT_FIRE ,MET_MONSTER{+0, +0, +0, -200},};public MarioStateMachine02() {this.score = 0;this.currentState = SMALL;}/*** 获得蘑菇,变成超级 super, 并加100积分*/public void obtainMushRoom() {executeEvent(GOT_MUSHROOM);}/*** 获得斗篷,加200积分,变身 斗篷马里奥*/public void obtainCape() {executeEvent(GOT_CAPE);}/*** 获得火焰,+300分, 变成火焰马里奥*/public void obtainFireFlower() {executeEvent(GOT_FIRE);}/*** 遇到怪兽*/public void meetMonster() {executeEvent(MET_MONSTER);}private void executeEvent(MarioEvent event) {int stateRow = currentState.getValue();int eventRow = event.getValue();this.currentState = transitionTable[stateRow][eventRow];this.score = actionTable[stateRow][eventRow];}public int getScore() {return score;}public MarioState getCurrentState() {return currentState;}
}
版本三:状态模式

完整代码。

当涉及到复杂的操作,就需要用到状态模式了。

/*** 所有状态类的接口* @Date 2022/10/27* @Author lifei*/
public interface IMario {// 当前状态的名称MarioState getName();// 以下是定义的事件void obtainMushRoom();void obtainCape();void obtainFireFlower();void meetMonster();
}/*** @Date 2022/10/27* @Author lifei*/
public class SmallMario implements IMario{private MarioStateMachine03 marioStateMachine;public SmallMario(MarioStateMachine03 marioStateMachine) {this.marioStateMachine = marioStateMachine;}@Overridepublic MarioState getName() {return MarioState.SMALL;}@Overridepublic void obtainMushRoom() {marioStateMachine.setMarioState(marioStateMachine.getSuperState());marioStateMachine.setScore(marioStateMachine.getScore() + 100);}@Overridepublic void obtainCape() {marioStateMachine.setMarioState(marioStateMachine.getCapeState());marioStateMachine.setScore(marioStateMachine.getScore() + 200);}@Overridepublic void obtainFireFlower() {marioStateMachine.setMarioState(marioStateMachine.getFireFlowerState());marioStateMachine.setScore(marioStateMachine.getScore() + 300);}@Overridepublic void meetMonster() {// 什么也不做}
}
/*** 状态模式的状态机* @Date 2022/10/27* @Author lifei*/
public class MarioStateMachine03 {private IMario smallState;private IMario superState;private IMario capeState;private IMario fireFlowerState;private IMario marioState;private int score;public MarioStateMachine03() {this.smallState = new SmallMario(this);this.superState = new SuperMario(this);this.capeState  = new CapeMario(this);this.fireFlowerState = new FireFlowerMario(this);this.marioState = smallState;}public MarioState getCurrentState() {return marioState.getName();}public IMario getMarioState() {return marioState;}public void setMarioState(IMario marioState) {this.marioState = marioState;}public IMario getSmallState() {return smallState;}public IMario getSuperState() {return superState;}public IMario getCapeState() {return capeState;}public IMario getFireFlowerState() {return fireFlowerState;}public int getScore() {return this.score;}public void setScore(int score) {this.score = score;}public void obtainMushRoom() {marioState.obtainMushRoom();}public void obtainCape() {marioState.obtainCape();}public void obtainFireFlower() {marioState.obtainFireFlower();}public void meetMonster() {marioState.meetMonster();}@Overridepublic String toString() {return MoreObjects.toStringHelper(MarioStateMachine03.class).add("marioState", marioState).add("score", score).toString();}
}

结合单例模式进行改造:

public interface IMario {MarioState getName();// 以下是定义的事件void obtainMushRoom(MarioStateMachine04 marioStateMachine);void obtainCape(MarioStateMachine04 marioStateMachine);void obtainFireFlower(MarioStateMachine04 marioStateMachine);void meetMonster(MarioStateMachine04 marioStateMachine);
}public class SmallMario implements IMario {private static final SmallMario instance = new SmallMario();private SmallMario(){}public static SmallMario getInstance() {return instance;}@Overridepublic MarioState getName() {return MarioState.SMALL;}@Overridepublic void obtainMushRoom(MarioStateMachine04 marioStateMachine) {marioStateMachine.setMarioState(SuperMario.getInstance());marioStateMachine.setScore(marioStateMachine.getScore() + 100);}@Overridepublic void obtainCape(MarioStateMachine04 marioStateMachine) {marioStateMachine.setMarioState(CapeMario.getInstance());marioStateMachine.setScore(marioStateMachine.getScore() + 200);}@Overridepublic void obtainFireFlower(MarioStateMachine04 marioStateMachine) {marioStateMachine.setMarioState(FireFlowerMario.getInstance());marioStateMachine.setScore(marioStateMachine.getScore() + 300);}@Overridepublic void meetMonster(MarioStateMachine04 marioStateMachine) {// 什么也不做}
}
public class MarioStateMachine04 {private IMario marioState;private int score;public MarioStateMachine04() {this.score = 0;this.marioState = SmallMario.getInstance();}public MarioState getCurrentState() {return marioState.getName();}public IMario getMarioState() {return marioState;}public void setMarioState(IMario marioState) {this.marioState = marioState;}public int getScore() {return this.score;}public void setScore(int score) {this.score = score;}public void obtainMushRoom() {marioState.obtainMushRoom(this);}public void obtainCape() {marioState.obtainCape(this);}public void obtainFireFlower() {marioState.obtainFireFlower(this);}public void meetMonster() {marioState.meetMonster(this);}
}

3.10 策略模式

策略模式:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

(1)示例一:策略模式的体现

定义一个算法族:

/*** 一个飞的行为* @Date 2022/10/23* @Author lifei*/
public interface FlyBehavior {void fly();
}/*** 一个具体fly的行为* @Date 2022/10/23* @Author lifei*/
public class FlyNoWay implements FlyBehavior{@Overridepublic void fly() {System.out.println("FlyNoWay: fly...0");}
}/*** 一个具体的行为* @Date 2022/10/23* @Author lifei*/
public class FlyWithWings implements FlyBehavior{@Overridepublic void fly() {System.out.println("FlyWithWings: fly 1,2,3");}
}

使用策略:

/*** @Date 2022/10/23* @Author lifei*/
public abstract class Duck {protected FlyBehavior flyBehavior;public void performFly() {flyBehavior.fly();}public void setFlyBehavior(FlyBehavior flyBehavior) {this.flyBehavior = flyBehavior;}
}/*** @Date 2022/10/23* @Author lifei*/
public class MallardDuck extends Duck{public MallardDuck() {this.flyBehavior = new FlyNoWay();}}

测试:

public class StrategyMain {public static void main(String[] args) {Duck duck = new MallardDuck();duck.performFly();duck.setFlyBehavior(new FlyWithWings());duck.performFly();}
}

(2)示例二:对一个文件内容进行排序的代码演进

第一版:为使用设计模式之前
/*** 文件排序* @Date 2022/10/23* @Author lifei*/
public class FileSorter {private static final long GB = 1024 * 1024 * 1024;public void sort(String filePath) {// 省略校验逻辑File file = new File(filePath);long fileSize = file.length();if (fileSize < 6 * GB) {quickSort(filePath);} else if (fileSize < 10 * GB) {externalSort(filePath);} else if (fileSize < 100 * GB) {concurrentExternalSort(filePath);} else {mapReduceSort(filePath);}}private void mapReduceSort(String filePath) {System.out.println("当文件超级大的时候,使用真正的Map-Reduce排序");}private void concurrentExternalSort(String filePath) {System.out.println("当文件过大当时候,使用并发当外部排序");}private void externalSort(String filePath) {System.out.println("当文件比较大当时候,使用外部排序");}private void quickSort(String filePath) {System.out.println("当文件不是很大当时候,使用快速排序");}
}
第二版:定义出排序算法族(将策略的定义分离出来)
public interface ISortAlg {void sort(String filePath);
}public class QuickSort implements ISortAlg{@Overridepublic void sort(String filePath) {System.out.println("当文件不是很大当时候,使用快速排序");}
}public class ExternalSort implements ISortAlg{@Overridepublic void sort(String filePath) {System.out.println("当文件比较大当时候,使用外部排序");}
}public class ConcurrentExternalSort implements ISortAlg{@Overridepublic void sort(String filePath) {System.out.println("当文件过大当时候,使用并发当外部排序");}
}public class MapReduceSort implements ISortAlg{@Overridepublic void sort(String filePath) {System.out.println("当文件超级大的时候,使用真正的Map-Reduce排序");}
}

使用:

public class FileSorter {private static final long GB = 1024 * 1024 * 1024;public void sort(String filePath) {// 省略校验逻辑File file = new File(filePath);long fileSize = file.length();ISortAlg sortAlg;if (fileSize < 6 * GB) {sortAlg = new QuickSort();} else if (fileSize < 10 * GB) {sortAlg = new ExternalSort();} else if (fileSize < 100 * GB) {sortAlg = new ConcurrentExternalSort();} else {sortAlg = new MapReduceSort();}sortAlg.sort(filePath);}
}
第三版:使用工厂模式对算法进行封装
/*** 算法的工厂模式* @Date 2022/10/23* @Author lifei*/
public class SortAlgFactory {private static final Map<String, ISortAlg> algs = new HashMap<>();static {algs.put("QuickSort", new QuickSort());algs.put("ExternalSort", new ExternalSort());algs.put("ConcurrentExternalSort", new ConcurrentExternalSort());algs.put("MapReduceSort", new MapReduceSort());}public static ISortAlg getSortAlg(String type) {if (StringUtils.isBlank(type)) {throw new IllegalArgumentException("类型不能为空");}return algs.get(type);}
}
public class FileSorter {private static final long GB = 1024 * 1024 * 1024;public void sort(String filePath) {// 省略校验逻辑File file = new File(filePath);long fileSize = file.length();ISortAlg sortAlg;if (fileSize < 6 * GB) {sortAlg = SortAlgFactory.getSortAlg("QuickSort");} else if (fileSize < 10 * GB) {sortAlg = SortAlgFactory.getSortAlg("ExternalSort");} else if (fileSize < 100 * GB) {sortAlg = SortAlgFactory.getSortAlg("ConcurrentExternalSort");} else {sortAlg = SortAlgFactory.getSortAlg("MapReduceSort");}sortAlg.sort(filePath);}
}
第四版:借助查表法,去除if-else判断
public class SortAlgRange {private final Range<Long> range;private final ISortAlg sortAlg;public SortAlgRange(Range<Long> range, ISortAlg sortAlg) {this.range = range;this.sortAlg = sortAlg;}public boolean isRange(long size) {return range.contains(size);}public ISortAlg getSortAlg() {return sortAlg;}
}
public class SortAlgRangeFactory {private static final long GB = 1024 * 1024 * 1024;private static final List<SortAlgRange> sortAlgRanges = new ArrayList<>();static {sortAlgRanges.add(new SortAlgRange(Range.<Long>closed(0l, 6*GB), new QuickSort()));sortAlgRanges.add(new SortAlgRange(Range.<Long>openClosed(6*GB, 10 * GB), new ExternalSort()));sortAlgRanges.add(new SortAlgRange(Range.<Long>openClosed(10 * GB, 100 * GB), new ConcurrentExternalSort()));sortAlgRanges.add(new SortAlgRange(Range.<Long>greaterThan(100 * GB), new MapReduceSort()));}public static ISortAlg getSortAlg(long size) {for (SortAlgRange sortAlgRange : sortAlgRanges) {if (sortAlgRange.isRange(size)) {return sortAlgRange.getSortAlg();}}return sortAlgRanges.get(sortAlgRanges.size()-1).getSortAlg();}public static void main(String[] args) {for (SortAlgRange sortAlgRange : sortAlgRanges) {System.out.println(sortAlgRange);}}
}
public class FileSorter {private static final long GB = 1024 * 1024 * 1024;public void sort(String filePath) {// 省略校验逻辑File file = new File(filePath);long fileSize = file.length();ISortAlg sortAlg;sortAlg = SortAlgRangeFactory.getSortAlg(fileSize);sortAlg.sort(filePath);}
}

3.11 访问者模式

访问者模式:允许一个或多个操作应用到一组对象上,解耦操作和对象本身。

函数重载在大部分面向对象编程语言中是静态绑定的。也就是说,调用类的哪个重载函数,是在编译期间,由参数的声明类型决定的,而非运行时,根据参数的实际类型决定的。

(1)访问者模式的演变示例:提取不同类型文件内容信息到txt文件

版本一:不同子类负责不同的功能
/*** 资源文件的抽象* @Date 2022/10/30* @Author lifei*/
public abstract class ResourceFile {protected String filePath;public ResourceFile(String filePath) {this.filePath = filePath;}// 提取文件内容到txt文件public abstract void extract2txt();
}/*** 提取word文件内容到txt* @Date 2022/10/30* @Author lifei*/
public class WordFile extends ResourceFile{public WordFile(String filePath) {super(filePath);}@Overridepublic void extract2txt() {System.out.println("提取Word文件内容到txt文件");}
}/*** 提取pdf文件内容到txt* @Date 2022/10/30* @Author lifei*/
public class PdfFile extends ResourceFile{public PdfFile(String filePath) {super(filePath);}@Overridepublic void extract2txt() {System.out.println("提取Pdf文件内容到txt");}
}/*** 抽取PPT文件内容到txt* @Date 2022/10/30* @Author lifei*/
public class PPTFile extends ResourceFile{public PPTFile(String filePath) {super(filePath);}@Overridepublic void extract2txt() {System.out.println("提取PPT文件内容......");}
}

测试:

/*** 测试* @Date 2022/10/30* @Author lifei*/
public class ToolApplication {public static void main(String[] args) {List<ResourceFile> resourceFiles = listAllResourceFiles();for (ResourceFile resourceFile : resourceFiles) {resourceFile.extract2txt();}}private static List<ResourceFile> listAllResourceFiles() {List<ResourceFile> result = new ArrayList<>();result.add(new PdfFile("a.pdf"));result.add(new WordFile("b.docx"));result.add(new PPTFile("c.pptx"));return result;}
}
版本二:业务操作跟具体数据结构解耦,利用重载

数据结构:

/*** 资源文件* @Date 2022/10/30* @Author lifei*/
public abstract class ResourceFile {protected final String filePath;public ResourceFile(String filePath) {this.filePath = filePath;}public String getFilePath() {return filePath;}
}/*** PDF文件* @Date 2022/10/30* @Author lifei*/
public class PdfFile extends ResourceFile{public PdfFile(String filePath) {super(filePath);}
}/*** PPT 文件* @Date 2022/10/30* @Author lifei*/
public class PPTFile extends ResourceFile{public PPTFile(String filePath) {super(filePath);}
}/*** word 文件* @Date 2022/10/30* @Author lifei*/
public class WordFile extends ResourceFile{public WordFile(String filePath) {super(filePath);}
}

行为:

/*** 提取功能: 使用函数的重载* @Date 2022/10/30* @Author lifei*/
public class Extractor {public void extract2txt(PdfFile pdfFile) {System.out.println("提取PDF 文件内容....");}public void extract2txt(PPTFile pptFile) {System.out.println("提取PPT文件内容......");}public void extract2txt(WordFile wordFile) {System.out.println("提取word文件内容.....");}
}

应用: 编译不通过

/*** 测试:行为和数据结构分离*   多态是一种动态绑定,可以在运行的时候获取对象的实际类型,来运行实际类型对应的方法。*   而函数重载是一种静态绑定,是在编译期间,由参数的声明类型决定的,而非运行时,根据参数的实际类型决定的。* @Date 2022/10/30* @Author lifei*/
public class ToolApplication {public static void main(String[] args) {Extractor extractor = new Extractor();List<ResourceFile> resourceFiles = listAllResourceFiles();for (ResourceFile resourceFile : resourceFiles) {// 利用函数重载,下面这句话编译不能通过extractor.extract2txt(resourceFile);}}private static List<ResourceFile> listAllResourceFiles() {List<ResourceFile> result = new ArrayList<>();result.add(new PdfFile("a.pdf"));result.add(new WordFile("b.docx"));result.add(new PPTFile("c.pptx"));return result;}
}
版本三:向访问者模式跨出重要一步

资源文件

/*** 资源文件* @Date 2022/10/30* @Author lifei*/
public abstract class ResourceFile {protected String filePath;public ResourceFile(String filePath) {this.filePath = filePath;}public abstract void accept(Extractor extractor);
}/*** PDF文件* @Date 2022/10/30* @Author lifei*/
public class PdfFile extends ResourceFile{public PdfFile(String filePath) {super(filePath);}@Overridepublic void accept(Extractor extractor) {extractor.extract2txt(this);}
}/*** PPT文件* @Date 2022/10/30* @Author lifei*/
public class PPTFile extends ResourceFile{public PPTFile(String filePath) {super(filePath);}@Overridepublic void accept(Extractor extractor) {extractor.extract2txt(this);}
}/*** word文件* @Date 2022/10/30* @Author lifei*/
public class WordFile extends ResourceFile{public WordFile(String filePath) {super(filePath);}@Overridepublic void accept(Extractor extractor) {extractor.extract2txt(this);}
}

行为:

/*** 提取功能: 使用函数的重载* @Date 2022/10/30* @Author lifei*/
public class Extractor {public void extract2txt(PdfFile pdfFile) {System.out.println("提取PDF 文件内容....");}public void extract2txt(PPTFile pptFile) {System.out.println("提取PPT文件内容......");}public void extract2txt(WordFile wordFile) {System.out.println("提取word文件内容.....");}
}

应用:

/*** 使用访问者模式* @Date 2022/10/30* @Author lifei*/
public class ToolApplication {public static void main(String[] args) {Extractor extractor = new Extractor();List<ResourceFile> resourceFiles = listAllResourceFiles();for (ResourceFile resourceFile : resourceFiles) {resourceFile.accept(extractor);}}private static List<ResourceFile> listAllResourceFiles() {List<ResourceFile> result = new ArrayList<>();result.add(new PdfFile("a.pdf"));result.add(new WordFile("b.docx"));result.add(new PPTFile("c.pptx"));return result;}
}

这个时候,如果添加新功鞥,仍然需要修改每个资源文件,违反了开闭原则。比如:添加一个压缩功能

/*** 压缩功能* @Date 2022/10/30* @Author lifei*/
public class Compressor {public void compressor(PdfFile pdfFile) {System.out.println("压缩pdf...");}public void compressor(PPTFile pdfFile) {System.out.println("压缩PPT...");}public void compressor(WordFile pdfFile) {System.out.println("压缩word...");}
}

修改所有的资源文件:

/*** 资源文件* @Date 2022/10/30* @Author lifei*/
public abstract class ResourceFile {protected String filePath;public ResourceFile(String filePath) {this.filePath = filePath;}public abstract void accept(Extractor extractor);public abstract void accept(Compressor compressor);
}/*** PDF文件* @Date 2022/10/30* @Author lifei*/
public class PdfFile extends ResourceFile{public PdfFile(String filePath) {super(filePath);}@Overridepublic void accept(Extractor extractor) {extractor.extract2txt(this);}@Overridepublic void accept(Compressor compressor) {compressor.compressor(this);}
}/*** PPT文件* @Date 2022/10/30* @Author lifei*/
public class PPTFile extends ResourceFile{public PPTFile(String filePath) {super(filePath);}@Overridepublic void accept(Extractor extractor) {extractor.extract2txt(this);}@Overridepublic void accept(Compressor compressor) {compressor.compressor(this);}
}/*** word文件* @Date 2022/10/30* @Author lifei*/
public class WordFile extends ResourceFile{public WordFile(String filePath) {super(filePath);}@Overridepublic void accept(Extractor extractor) {extractor.extract2txt(this);}@Overridepublic void accept(Compressor compressor) {compressor.compressor(this);}
}

应用:

/*** 使用访问者模式* @Date 2022/10/30* @Author lifei*/
public class ToolApplication {public static void main(String[] args) {Extractor extractor = new Extractor();Compressor compressor = new Compressor();List<ResourceFile> resourceFiles = listAllResourceFiles();for (ResourceFile resourceFile : resourceFiles) {// 执行下面这行代码的时候,会根据多台调用实际类型的accept函数resourceFile.accept(extractor);resourceFile.accept(compressor);}}private static List<ResourceFile> listAllResourceFiles() {List<ResourceFile> result = new ArrayList<>();result.add(new PdfFile("a.pdf"));result.add(new WordFile("b.docx"));result.add(new PPTFile("c.pptx"));return result;}
}
版本四:将行为进行抽象出Visitor,完成访问者模式

行为抽象:visitor

public interface Visitor {void visitor(PdfFile pdfFile);void visitor(PPTFile pptFile);void visitor(WordFile wordFile);
}/*** 压缩功能* @Date 2022/10/30* @Author lifei*/
public class Compressor implements Visitor{@Overridepublic void visitor(PdfFile pdfFile) {System.out.println("压缩pdf...");}@Overridepublic void visitor(PPTFile pdfFile) {System.out.println("压缩PPT...");}@Overridepublic void visitor(WordFile pdfFile) {System.out.println("压缩word...");}
}/*** 提取功能: 使用函数的重载* @Date 2022/10/30* @Author lifei*/
public class Extractor implements Visitor{@Overridepublic void visitor(PdfFile pdfFile) {System.out.println("提取PDF 文件内容....");}@Overridepublic void visitor(PPTFile pptFile) {System.out.println("提取PPT文件内容......");}@Overridepublic void visitor(WordFile wordFile) {System.out.println("提取word文件内容.....");}
}

数据结构:

/*** 资源文件* @Date 2022/10/30* @Author lifei*/
public abstract class ResourceFile {protected String filePath;public ResourceFile(String filePath) {this.filePath = filePath;}public abstract void accept(Visitor visitor);}/*** PDF文件* @Date 2022/10/30* @Author lifei*/
public class PdfFile extends ResourceFile {public PdfFile(String filePath) {super(filePath);}@Overridepublic void accept(Visitor visitor) {visitor.visitor(this);}
}/*** PPT文件* @Date 2022/10/30* @Author lifei*/
public class PPTFile extends ResourceFile {public PPTFile(String filePath) {super(filePath);}@Overridepublic void accept(Visitor visitor) {visitor.visitor(this);}
}/*** word文件* @Date 2022/10/30* @Author lifei*/
public class WordFile extends ResourceFile {public WordFile(String filePath) {super(filePath);}@Overridepublic void accept(Visitor visitor) {visitor.visitor(this);}
}

应用:

/*** 使用访问者模式* @Date 2022/10/30* @Author lifei*/
public class ToolApplication {public static void main(String[] args) {Extractor extractor = new Extractor();Compressor compressor = new Compressor();List<ResourceFile> resourceFiles = listAllResourceFiles();for (ResourceFile resourceFile : resourceFiles) {// 执行下面这行代码的时候,会根据多台调用实际类型的accept函数resourceFile.accept(extractor);resourceFile.accept(compressor);}}private static List<ResourceFile> listAllResourceFiles() {List<ResourceFile> result = new ArrayList<>();result.add(new PdfFile("a.pdf"));result.add(new WordFile("b.docx"));result.add(new PPTFile("c.pptx"));return result;}
}

(2)Single Dispatch 和 Double Dispatch

所谓 Single Dispatch,指的是执行哪个对象的方法,根据对象的运行时类型来决定;执行对象的哪个方法,根据方法参数的编译时类型来决定。所谓 Double Dispatch,指的是执行哪个对象的方法,根据对象的运行时类型来决定;执行对象的哪个方法,根据方法参数的运行时类型来决定。

具体到编程语言的语法机制,Single Dispatch 和 Double Dispatch 跟多态和函数重载直接相关。当前主流的面向对象编程语言(比如,Java、C++、C#)都只支持 Single Dispatch,不支持 Double Dispatch。

因此 支持双分派的语言不需要访问者模式。

/*** 父类* @Date 2022/10/30* @Author lifei*/
public class ParentClass {public void f() {System.out.println("I'm ParentClass's f() ");}
}/*** @Date 2022/10/30* @Author lifei*/
public class ChildClass extends ParentClass{@Overridepublic void f() {System.out.println("I'm ChildClass's f()");}
}/*** 验证单分工* @Date 2022/10/30* @Author lifei*/
public class SingleDispatchClass {public void polymorphismFunction(ParentClass p) {p.f();}public void overloadFunction(ParentClass p) {//        p.f();System.out.println("I am overloadFunction(ParentClass p).");}public void overloadFunction(ChildClass c) {//        c.f();System.out.println("I am overloadFunction(ChildClass c).");}
}

(3)使用工厂模式实现案例:提取不同类型文件内容信息到txt文件

数据结构:

/*** @Date 2022/10/30* @Author lifei*/
public abstract class ResourceFile {private String filePath;public ResourceFile(String filePath) {this.filePath = filePath;}public String getFilePath() {return filePath;}public abstract ResourceFileType getType();
}/*** @Date 2022/10/30* @Author lifei*/
public class PdfFile extends ResourceFile{public PdfFile(String filePath) {super(filePath);}@Overridepublic ResourceFileType getType() {return ResourceFileType.PDF;}
}/*** @Date 2022/10/30* @Author lifei*/
public class PPTFile extends ResourceFile{public PPTFile(String filePath) {super(filePath);}@Overridepublic ResourceFileType getType() {return ResourceFileType.PPT;}
}/*** @Date 2022/10/30* @Author lifei*/
public class WordFile extends ResourceFile{public WordFile(String filePath) {super(filePath);}@Overridepublic ResourceFileType getType() {return ResourceFileType.WORD;}
}

行为:

/*** @Date 2022/10/30* @Author lifei*/
public interface Extractor {void extract2txt(ResourceFile resourceFile);
}/*** @Date 2022/10/30* @Author lifei*/
public class PDFExtractor implements Extractor{@Overridepublic void extract2txt(ResourceFile resourceFile) {System.out.println("提取PDF文件内容......");}
}/*** @Date 2022/10/30* @Author lifei*/
public class PPTExtractor implements Extractor{@Overridepublic void extract2txt(ResourceFile resourceFile) {System.out.println("提取PPT文件内容......");}
}

工厂:

/*** @Date 2022/10/30* @Author lifei*/
public class ExtractorFactory {private static final Map<ResourceFileType, Extractor> extractors = new HashMap<>();static {extractors.put(ResourceFileType.PDF, new PDFExtractor());extractors.put(ResourceFileType.PPT, new PPTExtractor());extractors.put(ResourceFileType.WORD, new WordExtractor());}public static Extractor getExtractor(ResourceFileType type) {return extractors.get(type);}
}

应用:

/*** @Date 2022/10/30* @Author lifei*/
public class ApplicationDemo {public static void main(String[] args) {List<ResourceFile> resourceFiles = listAllResourceFiles();for (ResourceFile resourceFile : resourceFiles) {Extractor extractor = ExtractorFactory.getExtractor(resourceFile.getType());extractor.extract2txt(resourceFile);}}private static List<ResourceFile> listAllResourceFiles() {List<ResourceFile> result = new ArrayList<>();result.add(new PdfFile("a.pdf"));result.add(new WordFile("b.docx"));result.add(new PPTFile("c.pptx"));return result;}
}

深入理解常见的二十三种设计模式相关推荐

  1. Java实现二十三种设计模式(五)—— 十一种行为型模式 (中)——解释器模式、迭代器模式、中介者模式、备忘录模式

    Java实现二十三种设计模式(五)-- 十一种行为型模式 (中)--解释器模式.迭代器模式.中介者模式.备忘录模式 一.解释器模式 我国 IT 界历来有一个汉语编程梦,虽然各方对于汉语编程争论不休,甚 ...

  2. 第二部分:二十三种设计模式解读——什么是工厂方法模式

    二十三种设计模式解读--什么是工厂方法模式 author:陈镇坤27 日期:2022年2月10日 修改日期:2022年6月23日 文章目录 二十三种设计模式解读--什么是工厂方法模式 一.工厂方法模式 ...

  3. 二十三种设计模式-六大原则

    二十三种设计模式 一.创建型: 单例模式.工厂模式.抽象工厂模式.原型模式.建造者模式: 二.结构型: 代理模式,装饰器模式.适配器模式.外观模式.组合模式.享元模式.桥梁模式: 三.行为型: 策略模 ...

  4. Java二十三种设计模式 之代理(proxy)

    Java二十三种设计模式 之代理(proxy) 今天我们学习一下静态代理和动态代理 我们来看代码(写一个坦克运行了多少时间): 第一种方法: public calss Tank implements ...

  5. 二十三种设计模式(第十二种)-----代理模式(Proxy)

    二十三种设计模式(第十二种)-----代理模式(Proxy) 尚硅谷视频连接https://www.bilibili.com/video/BV1G4411c7N4?from=search&se ...

  6. 二十三种设计模式之原型模式

    今天继续探讨GOF二十三种设计模式的原型模式,原型模式也是属于创建型模式的一种 原型模式通俗的讲就是对象复制的过程,即通过一个原型对象,我可以得到一个该对象的克隆. 下面来看下原型模式的第一种写法-- ...

  7. Java代码设计模式讲解二十三种设计模式

    设计模式 文章目录 设计模式 一.创造型设计模式 1.1 单例模式 1.1.1 饿汉式单例模式 1.1.2 懒汉式单例模式 (1)线程不安全的情况 (2)线程安全的情况 1. 实例化的方法上加sync ...

  8. Java 二十三种设计模式

    一.单例模式 定义 Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有 ...

  9. 二十三种设计模式简介及类图(转载)

    该文章转载于:https://www.cnblogs.com/pony1223/p/7608955.html 一.设计模式的三个分类 创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程. ...

最新文章

  1. Linux使用Jexus托管Asp.Net Core应用程序
  2. having vs where
  3. mariadb mysql 配置文件_MariaDB/MySQL配置文件my.cnf解读
  4. Linux 实操———CentOS 6 安装配置 Tomcat
  5. 员工离职时被HR回复“猝死了再说”!HR道歉:没控制好情绪
  6. 独立物理机和虚拟机比较有什么优势?
  7. [BAT] 执行xcopy命令后出现Invalid num of parameters错误的解决办法
  8. Ubuntu下替换软件列表
  9. 基于SSM+Eclipse+MySQL的学生宿舍管理系统(SSM毕业设计源码)(学生宿舍管理系统毕业设计)
  10. javacv使用笔记
  11. 九酷音乐真实地址解析
  12. 【Excel VBA】一键取消excel中所有隐藏sheet
  13. 编码解码--url编码解码
  14. 基于Web SCADA平台构建实时数字化产线 - 初篇
  15. 使用UnblockNeteaseMusic播放网易云音乐客户端无版权歌曲
  16. 微信支付-vue 实现微信支付-前端篇
  17. 笨办法学python练习35分支与函数
  18. python基本算法合集(14)——计算从1加到100之和
  19. BLE连接中的中心设备与外围设备
  20. 文存阅刊杂志文存阅刊杂志社文存阅刊编辑部2023年第1期目录

热门文章

  1. 《给业余投资者的10条军规 (雪球「岛」系列) (闲来一坐s话投资》读书笔记
  2. Effective C++ 条款14
  3. 深入ES6:箭头函数
  4. oracle home 命令,oracle基本命令集锦
  5. zookeeper 企业面试真题
  6. 《我是一只IT小小鸟》读书笔记(2)
  7. Matlab 定点化函数fi
  8. php计算股票均线,移动平均线——Moving Average 平均线的计算公式
  9. 线性代数的本质--笔记整理
  10. Paging3 分页库的使用