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

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

文章目录

一、设计模式的分类

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

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

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

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 装饰者模式

通过组合的方式。

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

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

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

应用:Java的IO

父类:

/**
 * 一个咖啡品牌店
 * @Date 2022/10/16
 * @Author lifei
 */
public abstract class Beverage {

    protected String description = "Unknown Beverage";

    public String getDescription() {
        return description;
    }

    public abstract double cost();
}

子类:某一个饮品

/**
 * 某一个饮品
 * @Date 2022/10/16
 * @Author lifei
 */
public class Espresso extends Beverage{

    public Espresso() {
        this.description = "Espresso";
    }
    @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

当调用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); => AObserverBObserver接收到消息
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;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值