深入理解常见的二十三种设计模式
深入理解常见的二十三种设计模式
文章目录
- 深入理解常见的二十三种设计模式
- 一、设计模式的分类
- 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 装饰者模式
通过组合的方式。
装饰者模式:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
装饰器模式相对于简单的组合关系,有两个比较特殊的地方:
- 装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类;
- 装饰器类是对功能的增强;
应用: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;}
}
深入理解常见的二十三种设计模式相关推荐
- Java实现二十三种设计模式(五)—— 十一种行为型模式 (中)——解释器模式、迭代器模式、中介者模式、备忘录模式
Java实现二十三种设计模式(五)-- 十一种行为型模式 (中)--解释器模式.迭代器模式.中介者模式.备忘录模式 一.解释器模式 我国 IT 界历来有一个汉语编程梦,虽然各方对于汉语编程争论不休,甚 ...
- 第二部分:二十三种设计模式解读——什么是工厂方法模式
二十三种设计模式解读--什么是工厂方法模式 author:陈镇坤27 日期:2022年2月10日 修改日期:2022年6月23日 文章目录 二十三种设计模式解读--什么是工厂方法模式 一.工厂方法模式 ...
- 二十三种设计模式-六大原则
二十三种设计模式 一.创建型: 单例模式.工厂模式.抽象工厂模式.原型模式.建造者模式: 二.结构型: 代理模式,装饰器模式.适配器模式.外观模式.组合模式.享元模式.桥梁模式: 三.行为型: 策略模 ...
- Java二十三种设计模式 之代理(proxy)
Java二十三种设计模式 之代理(proxy) 今天我们学习一下静态代理和动态代理 我们来看代码(写一个坦克运行了多少时间): 第一种方法: public calss Tank implements ...
- 二十三种设计模式(第十二种)-----代理模式(Proxy)
二十三种设计模式(第十二种)-----代理模式(Proxy) 尚硅谷视频连接https://www.bilibili.com/video/BV1G4411c7N4?from=search&se ...
- 二十三种设计模式之原型模式
今天继续探讨GOF二十三种设计模式的原型模式,原型模式也是属于创建型模式的一种 原型模式通俗的讲就是对象复制的过程,即通过一个原型对象,我可以得到一个该对象的克隆. 下面来看下原型模式的第一种写法-- ...
- Java代码设计模式讲解二十三种设计模式
设计模式 文章目录 设计模式 一.创造型设计模式 1.1 单例模式 1.1.1 饿汉式单例模式 1.1.2 懒汉式单例模式 (1)线程不安全的情况 (2)线程安全的情况 1. 实例化的方法上加sync ...
- Java 二十三种设计模式
一.单例模式 定义 Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有 ...
- 二十三种设计模式简介及类图(转载)
该文章转载于:https://www.cnblogs.com/pony1223/p/7608955.html 一.设计模式的三个分类 创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程. ...
最新文章
- Linux使用Jexus托管Asp.Net Core应用程序
- having vs where
- mariadb mysql 配置文件_MariaDB/MySQL配置文件my.cnf解读
- Linux 实操———CentOS 6 安装配置 Tomcat
- 员工离职时被HR回复“猝死了再说”!HR道歉:没控制好情绪
- 独立物理机和虚拟机比较有什么优势?
- [BAT] 执行xcopy命令后出现Invalid num of parameters错误的解决办法
- Ubuntu下替换软件列表
- 基于SSM+Eclipse+MySQL的学生宿舍管理系统(SSM毕业设计源码)(学生宿舍管理系统毕业设计)
- javacv使用笔记
- 九酷音乐真实地址解析
- 【Excel VBA】一键取消excel中所有隐藏sheet
- 编码解码--url编码解码
- 基于Web SCADA平台构建实时数字化产线 - 初篇
- 使用UnblockNeteaseMusic播放网易云音乐客户端无版权歌曲
- 微信支付-vue 实现微信支付-前端篇
- 笨办法学python练习35分支与函数
- python基本算法合集(14)——计算从1加到100之和
- BLE连接中的中心设备与外围设备
- 文存阅刊杂志文存阅刊杂志社文存阅刊编辑部2023年第1期目录