深入理解常见的二十三种设计模式
文章目录
一、设计模式的分类
设计模式要干的事情就是解耦,创建型模式是把创建和使用代码解耦,结构型模式是将不同够功能代码解耦,行为型模式是将不同的行为代码解耦。
设计原则和思想其实比设计模式更加普适重要,掌握了代码的设计原则和思想,我们甚至可以创造出来新的设计模式。
如果只掌握知识,没锻炼能力,遇到实际问题,遇到问题还是没法自己去分析、思考、解决。
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 {
@Override
Pizza 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 {
@Override
Pizza 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
@Override
public void prepare() {
this.ingredient01 = pizzaIngredientFactory.createIngredient01();
}
}
一个具体的配料工厂:
/**
* 抽象工厂的具体实现
*/
public class HeNanPizzaIngredientFactory implements PizzaIngredientFactory{
/**
* 生产 配料01
* @return
*/
@Override
public String createIngredient01() {
return "HeNan ingredient01";
}
/**
* 生产 配料 02
* @return
*/
@Override
public 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 {
@Override
AbstractPizza 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;
}
@Override
protected Builder self() {
return this;
}
// 协变返回类型:子类声明返回超级类中声明的返回类型的子类
// 它允许客户端无需类型转换就能使用这些了类型
@Override
protected 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
*/
@Override
public 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;
}
@Override
public boolean hasMoreElements() {
return iterator.hasNext();
}
@Override
public 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 {
@Override
public void a2() {
super.a1();
}
@Override
public 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";
}
@Override
public 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;
}
@Override
public double cost() {
return 0.20 + beverage.cost();
}
@Override
public 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.89
System.out.println(beverage.getDescription() + ", 价格为: " + beverage.cost());
// 添加摩卡
beverage = new Mocha(beverage);
// Espresso, Mocha, 价格为: 1.09
System.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;
@Override
public String getName() {
return name;
}
@Override
public 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;
}
@Override
public 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
*/
@Override
public 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); // false
System.out.println(val01 == val03); // ture
构造函数创建的方式,不会用到IntegerCache。
享元模式在Java String中的使用:
String str01 = "你好";
String str02 = "你好";
String str03 = new String("你好");
System.out.println(str01==str02); // true
System.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);
}
@Override
public int countNumberOfFiles() {
int res = 0;
for (FileSystemNodeParent fileDir : subNodes) {
res += fileDir.countNumberOfFiles();
}
return res;
}
@Override
public 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);
}
@Override
public int countNumberOfFiles() {
return 1;
}
@Override
public 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;
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
@Override
public boolean isVegetarian() {
return vegetarian;
}
@Override
public double getPrice() {
return price;
}
@Override
public 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;
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
@Override
public void add(MenuComponent menuComponent) {
this.menuComponents.add(menuComponent);
}
@Override
public void remove(MenuComponent menuComponent) {
this.menuComponents.remove(menuComponent);
}
@Override
public void getChild(int i) {
this.menuComponents.get(i);
}
@Override
public 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{
@Override
public void handle() {
boolean handled = false;
//.... 判断当前处理其链是否可以操作,如果可以操作将 handled设置为true
if (!handled && successor!=null) {
successor.handle();
}
}
}
/**
* 处理器B
* @Date 2022/10/23
* @Author lifei
*/
public class BHandler extends Handler{
@Override
public void handle() {
boolean handled = false;
//...... 判断当前处理其链是否可以操作,如果可以操作将 handled设置为true
if (!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{
@Override
protected boolean doHandle() {
boolean handled = false;
// 看是否能在当前处理器处理,如果能成功处理,返回true,否则返回false。
return handled;
}
}
public class BHandler extends Handler{
@Override
protected 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{
@Override
public boolean handle() {
boolean handled = false;
// ...... 判断当前处理其是否能处理,如果能处理返回ture,否则,返回false
return handled;
}
}
public class HandlerB implements IHandler{
@Override
public boolean handle() {
boolean handled = false;
// ...... 判断当前处理其是否能处理,如果能处理返回ture,否则,返回false
return 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{
@Override
public boolean doFilter(Context context) {
boolean legal = false;
// 判断是否合法
return legal;
}
}
/**
* 政治敏感词过滤
* @Date 2022/10/23
* @Author lifei
*/
public class PoliticalWordFilter extends SensitiveWordFilter{
@Override
public boolean doFilter(Context context) {
boolean legal = false;
// 过滤政治敏感词
return legal;
}
}
/**
* 广告法敏感词过滤
* @Date 2022/10/23
* @Author lifei
*/
public class AdsWordFilter extends SensitiveWordFilter{
@Override
public 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);
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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]);
}
@Override
public 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]);
}
@Override
public 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]);
}
@Override
public 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);
}
}
}
@Override
public 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));
}
}
@Override
public 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 == 200
public 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
*/
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public void execute() {
this.stereo.on();
this.stereo.setCD();
this.stereo.setVolume(11);
}
}
/**
* 不做任何功能的按钮
* 很多时候,空对象本身也被视为一种设计模式
* @Date 2022/11/5
* @Author lifei
*/
public class NoCommand implements Command {
@Override
public 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;
}
@Override
public void execute() {
this.light.off();
}
@Override
public 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;
}
@Override
public void execute() {
// 记录当前的速度,以便撤销的时候使用
this.prevSpeed = ceilingFan.getSpeed();
ceilingFan.high();
System.out.println("设置成高档位");
}
@Override
public 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);
}
@Override
public 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;
}
@Override
public 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);
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public void add(E obj) {
elements[totalSize] = obj;
addTimestamps[totalSize] = System.nanoTime();
delTimestamps[totalSize] = Long.MAX_VALUE;
totalSize++;
actualSize++;
}
@Override
public 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];
}
@Override
public 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();
}
@Override
public boolean hasNext() {
return this.leftCount>0;
}
@Override
public 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;
@Override
public void registerObserver(Observer observer) {
observerList.add(observer);
}
@Override
public void removeObserver(Observer observer) {
int i = observerList.indexOf(observer);
if (i>=0) {
observerList.remove(i);
}
}
@Override
public 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);
}
@Override
public void display() {
System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");
}
@Override
public 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);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherDataObservable) {
WeatherDataObservable weatherDataObservable = (WeatherDataObservable) o;
this.temperature = weatherDataObservable.getTemperature();
this.humidity = weatherDataObservable.getHumidity();
display();
}
}
@Override
public 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.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{
/**
* 添加一个订阅主题的竹梅
*/
@Subscribe
public 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{
/**
* 添加一个订阅主题的竹梅
*/
@Subscribe
public void display(WeatherDataEvent weatherDataEvent) {
System.out.println("Current conditions: " + weatherDataEvent.getTemperature() + "F degrees and "
+ weatherDataEvent.getHumidity() + "% humidity");
}
@Subscribe
public 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 {
@Override
public void brew() {
out.println("冲泡咖啡");
}
@Override
public void addCondiments() {
out.println("添加 牛奶");
}
/**
* 实现钩子方法
* @return
*/
@Override
public 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;
}
@Override
public void insertQuarter() {
System.out.println("已经售罄,不能投入硬币");
}
@Override
public void ejectQuarter() {
System.out.println("已经售罄,没有投入硬币!");
}
@Override
public void turnCrank() {
System.out.println("你又转动了转轴,已经售罄了!");
}
@Override
public 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;
}
@Override
public void insertQuarter() {
System.out.println("你投入了一枚硬币.......");
gumballMachine.setGumballMachineState(gumballMachine.getHasQuarterState());
}
@Override
public void ejectQuarter() {
System.out.println("还没有投入硬币......");
}
@Override
public void turnCrank() {
System.out.println("已经转动转轴,但是还没有投入硬币......");
}
@Override
public 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;
}
@Override
public void insertQuarter() {
System.out.println("你已经投过硬币了......");
}
@Override
public void ejectQuarter() {
System.out.println("退出投入的硬币....");
this.gumballMachine.setGumballMachineState(gumballMachine.getNoQuarterState());
}
@Override
public 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());
}
}
@Override
public 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;
}
@Override
public void insertQuarter() {
System.out.println("硬币已经投过了,不需要再次投入硬币。请等待,我们将给你一个糖果!");
}
@Override
public void ejectQuarter() {
System.out.println("不能退出硬币,应为你已经转动了转轴!");
}
@Override
public void turnCrank() {
System.out.println("你又转动了转轴,但是不会产生更多的糖果......");
}
@Override
public 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;
}
@Override
public void insertQuarter() {
System.out.println("你已经中奖了,不需要投入硬币,我们即将给你糖果");
}
@Override
public void ejectQuarter() {
System.out.println("你已经中奖了,即将给你糖果,但是不能退出硬币给你。");
}
@Override
public void turnCrank() {
System.out.println("你转动了转轴,但是不会给你更多糖果。请稍等,会把奖励的糖果给你。");
}
@Override
public 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.value
private 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;
}
@Override
public MarioState getName() {
return MarioState.SMALL;
}
@Override
public void obtainMushRoom() {
marioStateMachine.setMarioState(marioStateMachine.getSuperState());
marioStateMachine.setScore(marioStateMachine.getScore() + 100);
}
@Override
public void obtainCape() {
marioStateMachine.setMarioState(marioStateMachine.getCapeState());
marioStateMachine.setScore(marioStateMachine.getScore() + 200);
}
@Override
public void obtainFireFlower() {
marioStateMachine.setMarioState(marioStateMachine.getFireFlowerState());
marioStateMachine.setScore(marioStateMachine.getScore() + 300);
}
@Override
public 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();
}
@Override
public 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;
}
@Override
public MarioState getName() {
return MarioState.SMALL;
}
@Override
public void obtainMushRoom(MarioStateMachine04 marioStateMachine) {
marioStateMachine.setMarioState(SuperMario.getInstance());
marioStateMachine.setScore(marioStateMachine.getScore() + 100);
}
@Override
public void obtainCape(MarioStateMachine04 marioStateMachine) {
marioStateMachine.setMarioState(CapeMario.getInstance());
marioStateMachine.setScore(marioStateMachine.getScore() + 200);
}
@Override
public void obtainFireFlower(MarioStateMachine04 marioStateMachine) {
marioStateMachine.setMarioState(FireFlowerMario.getInstance());
marioStateMachine.setScore(marioStateMachine.getScore() + 300);
}
@Override
public 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{
@Override
public void fly() {
System.out.println("FlyNoWay: fly...0");
}
}
/**
* 一个具体的行为
* @Date 2022/10/23
* @Author lifei
*/
public class FlyWithWings implements FlyBehavior{
@Override
public 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{
@Override
public void sort(String filePath) {
System.out.println("当文件不是很大当时候,使用快速排序");
}
}
public class ExternalSort implements ISortAlg{
@Override
public void sort(String filePath) {
System.out.println("当文件比较大当时候,使用外部排序");
}
}
public class ConcurrentExternalSort implements ISortAlg{
@Override
public void sort(String filePath) {
System.out.println("当文件过大当时候,使用并发当外部排序");
}
}
public class MapReduceSort implements ISortAlg{
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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);
}
@Override
public void accept(Extractor extractor) {
extractor.extract2txt(this);
}
@Override
public 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);
}
@Override
public void accept(Extractor extractor) {
extractor.extract2txt(this);
}
@Override
public 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);
}
@Override
public void accept(Extractor extractor) {
extractor.extract2txt(this);
}
@Override
public 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{
@Override
public void visitor(PdfFile pdfFile) {
System.out.println("压缩pdf...");
}
@Override
public void visitor(PPTFile pdfFile) {
System.out.println("压缩PPT...");
}
@Override
public void visitor(WordFile pdfFile) {
System.out.println("压缩word...");
}
}
/**
* 提取功能: 使用函数的重载
* @Date 2022/10/30
* @Author lifei
*/
public class Extractor implements Visitor{
@Override
public void visitor(PdfFile pdfFile) {
System.out.println("提取PDF 文件内容....");
}
@Override
public void visitor(PPTFile pptFile) {
System.out.println("提取PPT文件内容......");
}
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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{
@Override
public 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);
}
@Override
public ResourceFileType getType() {
return ResourceFileType.PDF;
}
}
/**
* @Date 2022/10/30
* @Author lifei
*/
public class PPTFile extends ResourceFile{
public PPTFile(String filePath) {
super(filePath);
}
@Override
public ResourceFileType getType() {
return ResourceFileType.PPT;
}
}
/**
* @Date 2022/10/30
* @Author lifei
*/
public class WordFile extends ResourceFile{
public WordFile(String filePath) {
super(filePath);
}
@Override
public 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{
@Override
public void extract2txt(ResourceFile resourceFile) {
System.out.println("提取PDF文件内容......");
}
}
/**
* @Date 2022/10/30
* @Author lifei
*/
public class PPTExtractor implements Extractor{
@Override
public 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;
}
}
10万+

被折叠的 条评论
为什么被折叠?



