【设计模式】笔记篇

目录标题

  • OO设计原则
  • 策略模式 - Strategy
    • 定义
    • 案例分析
      • 需求
      • 思路分析
      • 核心代码展示
      • 进一步优化
      • UML 图
  • 观察者模式 - Observe
    • 定义
    • 案例分析
      • 需求
      • UML图
      • 内置的Java观察者模式
      • 核心代码
    • 总结
  • 装饰者模式 - Decorator
    • 定义
    • 案例分析
      • 需求
      • UML图分析
      • 核心代码
    • 总结
  • 工厂模式 - Abstract Method/Factory Method
    • 定义
    • 案例分析
      • 简易工厂模式
        • 需求
        • UML图
        • 核心代码
      • 抽象工厂模式
        • 需求
        • UML图
        • 核心代码
    • 总结
  • 单例模式 - Singleton
    • 双重检验锁
    • 热实例化
    • 同步语句块
  • 命令模式 - Command
    • 定义
    • 案例分析
      • 需求
      • 分析
      • 核心代码
    • 总结
  • 适配器模式 - Adapter
    • 定义
    • 需求
      • 核心代码
  • 外观模式 - Facade
    • 定义
    • 核心代码
  • 模板方法模式 - Template Method
    • 定义
    • 核心代码
  • 迭代器模式 - Iterator
    • 定义
    • 核心代码
  • 组合模式 - Composite
    • 定义
    • 核心代码
  • 状态模式 - State
    • 定义
    • 核心代码
  • 代理模式 - Proxy
    • 定义
    • 需求
      • 核心代码
  • 总结
    • 思维导图
    • 模式的分类
      • 根据模式的目标分类
        • 创建型
        • 行为型
        • 结构型
      • 模式所处理的对象
        • 对象

代码仓库:hanliy/Head-First-DesignPatterns-Demos
书籍推荐:Head First设计模式(中文版)、 广受推荐的最新设计模式书籍:《深入设计模式》(需购买)
进阶推荐:Java Design Patterns

前言

这本书《Head First 设计模式(中文版)》看了一个月左右(2024年3月4号 ~ 2024年4月8号),采用大量的插图、图例来进行辅助讲解,插图设计的非常和内容贴切。入门级别,强烈推荐。

看完了以后,也写了一个小小的实战 【设计模式】实战篇,算是对自己这一个月的小总结。

等把这个 【设计模式】实战篇 写完,算是对自己前一个月的收尾。Orz,希望不会咕咕咕~

OO设计原则

  1. 封装变化
    • 找出应用中可能需要变化之处,把他们独立出来,不要和哪些不需要变化的代码混在一起
    • 分开变化和不会变化的部分:
    • 把会变化的部门取出来并封装起来,以便以后可以轻易的改动和扩充此部分,而不影响不需要变化的部分
  2. 多使用组合,少用继承
  3. 针对接口编程,而不是针对实现类

Before :子类的行为来自于继承某个接口的自行实现 —> 过于绑定了
After:子类使用接口表示行为,实际的 “实现” 不会绑定在子类中,特定的行为只在接口中的实现了中存在

  1. 为了交互对象之间的松耦合设计而努力。
  2. 开闭原则:类应该对扩展开放,对修改关闭

添加一个新的功能应该是,在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)

  1. 依赖倒置原则:依赖抽象,不要依赖具体的类
  2. 最少知识原则:减少对象之间的交互,只留下几个“密友”
  3. **好莱坞原则:“Don’t call me; I’ll call you.” **

好莱坞原则是用在创建框架或组件上的一种技巧,好让低层组件能够被挂钩进计算中,而且又不会让高层组件依赖低层组件。

  1. 单一原则:一个类应该只有一个引起变化的原因

好莱坞原则和依赖倒置原则之间的关系如何?
两者的目标都是在于解耦。
但是依赖倒置原则更加注重如何在设计中避免依赖。
好莱坞原则教我们一个技巧,创建一个有弹性的设计,允许低层结构能够互相操作,而又防止其他类太过依赖它们。

策略模式 - Strategy

定义

封装可互换的行为,然后使用委托来决定要采用哪一个行为
策略模式中,所组合的类实现了整个算法。
定义一个算法家族,并让这些算法可以互换。正因为每一个法都被封装起来了,所以客户可以轻易地使用不同的算法。

案例分析

需求

假定需要创建出很多个鸭子:绿头鸭、橡皮鸭、木头鸭等等。如何进行设计呢?

思路分析

设计分析


  1. 新建个 Duck 鸭子类,设为抽象类(提高代码的复用),具有 统一 的一些行为:display外观(设计为抽象方法)、swim 游泳(默认的实现方法)
  2. 考虑到 Duck 鸭子类, 有一些 变化 的地方:Fly飞翔、 Quack呱呱叫
  • FlyBehavior 接口, 不同的实现类实现接口中对 fly 飞翔行为的实现:FlyNoWayFlyWithWings
  • QuackBehavior 接口, 不同的实现类实现接口中对 quack 呱呱叫行为的实现: MuteQuackQuackSqueak
  1. Duck 鸭子类 加入两个实例变量 flyBehaviorquackBehavior,声明为接口类型,这样每个对象都会在运行的时候动态的引用正确的行为

具体实现
这样,假定我们需要 MallardDuck 绿头鸭类,那么在构建的时候,使用多态指定 FlyBehavior 接口 和 QuackBehavior 接口 的实现类

核心代码展示

/**
 * @author hanyl
 * @apiNote 鸭子抽象类
 * @date 2024/2/27 16:11
 */
public abstract class Duck {

    /**
     * 具有飞翔行为的FlyBehavior接口
     */
    FlyBehavior flyBehavior;

    /**
     * 具有呱呱叫行为的QuackBehavior接口
     */
    QuackBehavior quackBehavior;

    /**
     * 外观抽象类
     *
     */
    public abstract void display();

    /**
     * 默认实现的游泳方法
     */
    public void swim(){
        System.out.println("All ducks float, even decoys!");
    }

    /**
     * demo1.Duck 类不直接处理,委托给 quackBehavior 引用的对象
     */
    public void performQuack(){
        quackBehavior.quack();
    }

    /**
     * demo1.Duck 类不直接处理,委托给 flyBehavior 引用的对象
     */
    public void performFly(){
        flyBehavior.fly();
    }
}
/**
 * @author hanyl
 * @apiNote 绿头鸭
 * @date 2024/2/27 16:21
 */
public class MallardDuck extends Duck{

    public MallardDuck() {
        // `demo1.MallardDuck` 调用的是 FlyWithWings 去作为 FlyBehavior 的具体实现
        flyBehavior = new FlyWithWings();
        // `demo1.MallardDuck` 调用的是 Quack 去作为 QuackBehavior 的具体实现
        quackBehavior = new Quack();
    }

    @Override
    public void display() {
        System.out.println("I'm a real Mallard duck");
    }
}

进一步优化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在上面这个场景中,我们是通过构造方法实例化,那么如何实现动态的修改呢?
A:没错,可以为 Duck 鸭子抽象类中的 两个属性:FlyBehaviorQuackBehavior 进行属性的set 修改

public abstract class Duck {
    
    // ignore
    
    /**
     * 赋予能够动态的修改属性的能力:Fly
     * @param flyBehavior 新的Fly能力
     */
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    /**
     * 赋予能够动态的修改属性的能力:Quack
     * @param quackBehavior 新的Quack能力
     */
    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }
}    

测试

Duck modelDuck = new ModelDuck();
modelDuck.performFly();
// 动态修改属性
modelDuck.setFlyBehavior(new FlyRocketPowered());
modelDuck.performFly();

UML 图

Duck.png

观察者模式 - Observe

定义

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

帮助对象知悉现况,不会错过该对象感兴趣的事。对象在运行时可决定是否继续被通知。
image.png

案例分析

需求

根据气象站提供的实际气象数据WeatherData,追踪并展示气象信息。展示平台有:当前状况布告板、预测布告板、统计布告栏等。

UML图

image.png
实现后的 UML图
image.png

内置的Java观察者模式

image.png
实现后的 UML图
image.png
缺陷
Observable是一个类 :

  • Java不支持多重继承。 这限制了Observable的复用潜力
  • 只有继承了Observable, 才能够使用他的核心方法

核心代码

public class WeatherData extends Observable {

    /**
     * 温度
     */
    private float temperature;

    /**
     * 湿度
     */
    private float humidity;

    /**
     * 气压
     */
    private float pressure;

    /**
     * 我们的构造器不再需要为了
     * 记住观察者们而建立数据结
     * 构了。
     */
    public WeatherData() {
    }

    /**
     * 测试数据的改变
     */
    public void measurementsChanged() {
        // 标识:指示状态已经改变
        setChanged();
        notifyObservers();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}
public class CurrentConditionsDisplay implements Observer, DisplayElement {

    Observable observable;
    private float temperature;
    private float humidity;

    /**
     * 现在构造器需要一Observable当参数,
     * 并将CurrentConditionsDisplay对象登记成为观察者。
     * @param observable 订阅主题
     */
    public CurrentConditionsDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    /**
     * Observer 的方法重写:接受推送过来的信息
     */
    @Override
    public void update(Observable observable, Object arg) {
        if (observable instanceof WeatherData) {
            WeatherData weatherData = (WeatherData)observable;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            display();
        }
    }

    /**
     * DisplayElement 的方法重写
     */
    @Override
    public void display() {
        System.out.println("【Java内置版】Current conditions: " + temperature
                + "F degrees and " + humidity + "% humidity");
    }
}

总结

  • 观察者模式:在对象之间定义一对多的依赖,当一个对象改变状态的时候,依赖他的对象就回收到通知并自动更新消息
  • 消息的接受没有顺序性
  • 使用此模式时,你可从被观察者处推(push)或拉(pull)数据(然而,推的方式被认为更“正确”)。
  • 如果有必要的话,可以实现自己的Observable(类而不是接口)
  • Swing大量使用观察者模式:点击按钮后,让观察者感应到Swing组件的不同类型事件

装饰者模式 - Decorator

定义

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

装饰者和被装饰对象有相同的超类型。
你可以用一个或多个装饰者包装一个对象
装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。
image.png

  • Component:抽象组件
  • ConcreteComponent :具体组件
  • Decorator:抽象装饰器
  • ConcreteDecorator:具体的装饰器

案例分析

需求

咖啡店计算每种咖啡后的价格。饮料总共有多重基底:黑咖啡、拿铁等等,可以为每种基底加上辅料:奶泡、牛奶、豆浆等等。

UML图分析

  • Component:抽象组件 -> 基底 Beverage
  • ConcreteComponent :具体组件 -> 黑咖啡DarkRoast、拿铁
  • Decorator:抽象装饰器 -> 辅料CondimentDecorator
  • ConcreteDecorator:具体的装饰器 -> 奶泡、牛奶、豆浆Soy

image.png
转化后的UML图:
Beverage.png

核心代码

public abstract class Beverage {

    String description = "Unknown Beverage";

    public String getDescription() {
        return description;
    }

    /**
     * 计算花费费用
     *
     * @return 总共费用
     */
    public abstract double cost();

}
public class DarkRoast extends Beverage {

    public DarkRoast() {
        description = "DarkRoast";

    }
    @Override
    public double cost() {
        return .99;
    }
}
/**
 * 【抽象装饰者】必须让Condiment Decorator能
 * 够取代Beverage,所以将Condiment
 * Decorator扩展自 Beverage 类。
 */
public abstract class CondimentDecorator extends Beverage {

    Beverage beverage;

    /**
     * 所有的调料装饰者都必须重新实现getDescription()方法。
     * @return
     */
    @Override
    public abstract String getDescription();
}
/**
 * @author hanyl
 * @apiNote 豆浆 
 */
public class Soy extends CondimentDecorator {

	/**
	 * 用个实例变量用来记录被装饰者
	 * @param beverage 被装饰者
	 */
	public Soy(Beverage beverage) {
		this.beverage = beverage;
	}

	/**
	 * 完整的输出每个被装饰者的每个信息,
	 * 而不是仅仅描述当前的调料信息
	 * 
	 * @return
	 */
	@Override
	public String getDescription() {
		// 1. 先利用委托,得到一个描述信息
		// 2. 再进行附加
		return beverage.getDescription() + ", Soy";
	}

	
	@Override
	public double cost() {
		return .15 + beverage.cost();
	}
}

总结

  1. **装饰者模式:**动态地将责任附加到对象上。
  2. 可以透明的插入装饰者(也就是说我只需要构造的时候指明一下被装饰者)
  3. 插入了大量的小类(具体装饰器,具体组件信息),不容易轻松理解, 程序容易复杂

工厂模式 - Abstract Method/Factory Method

定义

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

案例分析

简易工厂模式

需求

有很多披萨店,不同区域的披萨店有不同的披萨类型。
披萨的制作需要进行的工作如下:准备、烘焙、剪切、装盒。
制作披萨需要:披萨名称、面团类型、酱料类型、一套佐料。
披萨店呢,提供给用户下单服务并制作披萨。
不同地区的披萨店,可以制作不同种类的披萨。根据用户的下单请求,来制作用户需要的披萨。

public class DependentPizzaStore {
 
	public Pizza createPizza(String style, String type) {
		Pizza pizza = null;
		if (style.equals("NY")) {
			if (type.equals("cheese")) {
				pizza = new NYStyleCheesePizza();
			} else if (type.equals("veggie")) {
				pizza = new NYStyleVeggiePizza();
			} else if (type.equals("clam")) {
				pizza = new NYStyleClamPizza();
			} else if (type.equals("pepperoni")) {
				pizza = new NYStylePepperoniPizza();
			}
		} else if (style.equals("Chicago")) {
			if (type.equals("cheese")) {
				pizza = new ChicagoStyleCheesePizza();
			} else if (type.equals("veggie")) {
				pizza = new ChicagoStyleVeggiePizza();
			} else if (type.equals("clam")) {
				pizza = new ChicagoStyleClamPizza();
			} else if (type.equals("pepperoni")) {
				pizza = new ChicagoStylePepperoniPizza();
			}
		} else {
			System.out.println("Error: invalid type of pizza");
			return null;
		}
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}
}

UML图

披萨抽象类:共同的拥有名称、面团类型、酱料类型、一套佐料等,他们都需要进行准备工作
烘焙、剪切、装盒等工作,不同的披萨店使用的原料和制作方法细节各有不同。
从上面的需求中,我们可以知道。我们是根据用户的需求,调用选择好了披萨原型以后,再去制作
商店抽象类:下单方法,制作抽象方法。
Pizza.png

核心代码
/**
 * @author hanyl
 * @apiNote 披萨抽象类
 * @date 2024/3/3 18:40
 */
public abstract class Pizza {
    /**
     * 名称
     */
    String name;

    /**
     * 面团
     */
    String dough;

    /**
     * 酱料
     */
    String sauce;

    /**
     * 佐料
     */
    List<String> toppings = new ArrayList<String>();

    /**
     * 准备工作
     */
    public void prepare() {
        System.out.println("Prepare " + name);
        System.out.println("Tossing dough...");
        System.out.println("Adding sauce...");
        System.out.println("Adding toppings: ");
        for (String topping : toppings) {
            System.out.println("   " + topping);
        }
    }

    /**
     * 烘焙
     */
    void bake() {
        System.out.println("Bake for 25 minutes at 350");
    }

    /**
     * 切片
     */
    void cut() {
        System.out.println("Cut the pizza into diagonal slices");
    }

    /**
     * 打包
     */
    void box() {
        System.out.println("Place pizza in official PizzaStore box");
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        StringBuffer display = new StringBuffer();
        display.append("---- " + name + " ----\n");
        display.append(dough + "\n");
        display.append(sauce + "\n");
        for (String topping : toppings) {
            display.append(topping + "\n");
        }
        return display.toString();
    }
}

/**
 * @author hanyl
 * @apiNote
 * @date 2024/3/3 22:02
 */
public abstract class PizzaStore {

    /**
     * 工厂方法。通过指定不同的参数,来决定构建哪种披萨店铺
     * @param item 指定创建的类型
     * @return
     */
    abstract Pizza createPizza(String item);

    /**
     * 下单接口
     * @param type 指定创建的类型
     * @return
     */
    public Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type);
        System.out.println("--- Making a " + pizza.getName() + " ---");
        /**
         * 共同方法
         */
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}
/**
 * 纽约风格的芝士披萨
 * @author 16248
 */
 * @author 16248
 */
public class NYStyleCheesePizza extends Pizza {

	/**
	 * 构造纽约风格的芝士披萨的方法
	 */
	public NYStyleCheesePizza() { 
		name = "NY Style Sauce and Cheese Pizza";
		dough = "Thin Crust Dough";
		sauce = "Marinara Sauce";
 
		toppings.add("Grated Reggiano Cheese");
	}
}
/**
 * 纽约地区的披萨店.
 * 不同地区的商店,能够制作出不同的披萨
 * @author 16248
 */
public class NYPizzaStore extends PizzaStore {

	/**
	 * 根据指明的类型。调用构造方法实例化对象
	 * @param item 指定创建的类型
	 * @return
	 */
	@Override
	Pizza createPizza(String item) {
		if (item.equals("cheese")) {
			return new NYStyleCheesePizza();
		} else if (item.equals("veggie")) {
			return new NYStyleVeggiePizza();
		} else if (item.equals("clam")) {
			return new NYStyleClamPizza();
		} else if (item.equals("pepperoni")) {
			return new NYStylePepperoniPizza();
		} else return null;
	}
}
public class PizzaTestDrive {

    public static void main(String[] args) {
        // 1. 用户选择在纽约披萨店下单
        PizzaStore nyStore = new NYPizzaStore();
        // 2. 用户在纽约披萨店下单的是 cheese 类型
        Pizza pizza1 = nyStore.orderPizza("cheese");
        // 3. 输出打印制作过程
        System.out.println("Ethan ordered a " + pizza1.getName() + "\n");
    }
}

抽象工厂模式

需求

在上面的基础上,不同的披萨构造的原料还不同。因此呢,我们还可以构建一个生产原料的工厂。
每种类型的披萨在不同地区的原料工厂需要的原料组成不同。
每种披萨都有面团、酱料、芝士、一些海鲜佐料。

UML图

Cheese.png

核心代码
public abstract class Pizza {

	protected String name;
	protected Dough dough;
	protected Sauce sauce;
	protected Veggies veggies[];
	protected Cheese cheese;
	protected Pepperoni pepperoni;
	protected Clams clam;

    /**
     * 由于每种披萨拥有不同的准备工作,因此改为抽象类
     */
	public abstract void prepare();

	void bake() {
		System.out.println("2. Bake for 25 minutes at 350");
	}

	void cut() {
		System.out.println("3. Cutting the pizza into diagonal slices");
	}

	void box() {
		System.out.println("4. Place pizza in official PizzaStore box");
	}

	public void setName(String name) {
		this.name = name;
	}

	String getName() {
		return name;
	}

	@Override
	public String toString() {
		StringBuffer result = new StringBuffer();
		result.append("---- " + name + " ----\n");
		if (dough != null) {
			result.append(dough);
			result.append("\n");
		}
		if (sauce != null) {
			result.append(sauce);
			result.append("\n");
		}
		if (cheese != null) {
			result.append(cheese);
			result.append("\n");
		}
		if (veggies != null) {
			for (int i = 0; i < veggies.length; i++) {
				result.append(veggies[i]);
				if (i < veggies.length-1) {
					result.append(", ");
				}
			}
			result.append("\n");
		}
		if (clam != null) {
			result.append(clam);
			result.append("\n");
		}
		if (pepperoni != null) {
			result.append(pepperoni);
			result.append("\n");
		}
		return result.toString();
	}
}
/**
 * @author hanyl
 * @apiNote 原料工厂类
 * @date 2024/3/4 13:56
 */
public interface PizzaIngredientFactory {

    /**
     * 制作面团
     *
     * @return Dough 面团
     */
    public Dough createDough();

    /**
     * 制作调味酱
     *
     * @return Sauce 调味酱
     */
    public Sauce createSauce();

    /**
     * 制作奶酪
     * @return Cheese 奶酪
     */
    public Cheese createCheese();

    /**
     * 蔬菜搭配
     * @return Veggies[] 蔬菜数据合集
     */
    public Veggies[] createVeggies();

    /**
     * 搭配香肠
     *
     * @return Pepperoni 香肠
     */
    public Pepperoni createPepperoni();

    /**
     * 制作蛤蜊
     *
     * @return Clams 蛤蜊
     */
    public Clams createClam();

}

这样,在具体的制作披萨过程,只需要指明一下我使用的是具体的哪一个原料工厂。

/**
 * @author hanyl
 * @apiNote 纽约地区的披萨原料制作工厂
 * @date 2024/3/4 14:00
 */
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {

    /**
     * 制作面团
     *
     * @return Dough 薄皮面团
     */
    @Override
    public Dough createDough() {
        return new ThinCrustDough();
    }

    /**
     * 制作调味酱
     *
     * @return Sauce 蕃茄酱
     */
    @Override
    public Sauce createSauce() {
        return new MarinaraSauce();
    }

    /**
     * 制作奶酪
     * @return Cheese 雷吉亚诺奶酪
     */
    @Override
    public Cheese createCheese() {
        return new ReggianoCheese();
    }

    /**
     * 蔬菜搭配
     * @return Veggies[] 蔬菜数据合集:大蒜、洋葱
     */
    @Override
    public Veggies[] createVeggies() {
        Veggies veggies[] = { new Garlic(), new Onion() };
        return veggies;
    }

    /**
     * 搭配香肠
     *
     * @return Pepperoni 薄片香肠
     */
    @Override
    public Pepperoni createPepperoni() {
        return new SlicedPepperoni();
    }

    /**
     * 制作蛤蜊
     *
     * @return Clams 新鲜蛤蜊
     */
    @Override
    public Clams createClam() {
        return new FreshClams();
    }
}
/**
 * 奶酪披萨
 * @author hanyl
 */
public class CheesePizza extends Pizza{

	PizzaIngredientFactory ingredientFactory;

	/**
	 * 通过不同的地区的原料工厂制作奶酪披萨
	 * @param ingredientFactory 不同的地区的原料工厂
	 */
	public CheesePizza(PizzaIngredientFactory ingredientFactory) {
		this.ingredientFactory = ingredientFactory;
	}

	/**
	 * 奶酪披萨:制作面团、抹上酱料、铺上奶酪
	 */
	@Override
	public void prepare() {
		System.out.println("1. Preparing " + name);
		dough = ingredientFactory.createDough();
		System.out.println("\t" + dough);
		sauce = ingredientFactory.createSauce();
		System.out.println("\t" +sauce);
		cheese = ingredientFactory.createCheese();
		System.out.println("\t" +cheese);
	}
}

接下来的话,我们开设纽约地区的披萨店:

  • 先选择纽约区域的原料工厂
  • 交由工厂类去制作这种类型的披萨
/**
 * @author hanyl
 * @apiNote 纽约披萨店
 * @date 2024/3/4 14:16
 */
public class NYPizzaStore extends PizzaStore {
    @Override
    protected Pizza createPizza(String item) {
        // 1. 需要返回的披萨
        Pizza pizza = null;
        // 2. 指定原料工厂为:纽约披萨原料工厂
        PizzaIngredientFactory nyPizzaIngredientFactory = new NYPizzaIngredientFactory();
        if (item.equals("cheese")){
            pizza = new CheesePizza(nyPizzaIngredientFactory);
            pizza.setName("New York Style Cheese Pizza");
        }else if (item.equals("clam")){
            pizza = new ClamPizza(nyPizzaIngredientFactory);
            pizza.setName("New York Style clam Pizza");
        }
        return pizza;
    }
}

总结

  1. 简单工厂,虽然不是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序从具体类解耦。
  2. 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象。

单例模式 - Singleton

单例模式:确保一个类只有一个实例,并提供全局访问点

在Java中实现单件模式需要私有的构造器、一个静态方法和一个静态变量。

双重检验锁

由于没有性能上的考虑,所以似乎杀鸡用了牛刀。适用于Jdk5 以上版本。

/**
 * @author hanyl
 * @apiNote
 * @date 2024/3/5 11:49
 */
public class Singleton {

    /**
     * 利用静态变量来记录唯一的实例
     */
    private volatile static Singleton singleton;

    /**
     * 构造器声明为私有的,这样就只有在内部类才能够实例化
     */
    private Singleton() {
    }

    /**
     * 获取唯一的实例化对象(双重检验锁)
     *
     * @return
     */
    public static Singleton getInstance() {
        // 如果静态唯一实例是空的,表示还没有创建实例
        if (singleton == null) {
            // 注意,只有第一次才彻底执行synchronized这里的代码。
            synchronized (Singleton.class) {
                if (singleton == null) {
                    // 而如果它不存在,我们就利用私有的构造器创建一个实例,并把它赋值给静态变量中
                    // “延迟实例化” : 也就是说:如果我们不调用这个方法,也就是永远不会实例化
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

热实例化

适用于一定要产生一个实例对象的场景。

/**
 * @author hanyl
 * @apiNote
 * @date 2024/3/5 11:49
 */
public class Singleton {

    /**
     * 利用静态变量来记录唯一的实例
     */
    private static Singleton singleton = new Singleton();

    /**
     * 构造器声明为私有的,这样就只有在内部类才能够实例化
     */
    private Singleton() {
    }

    /**
     * 获取唯一的实例化对象(双重检验锁)
     *
     * @return
     */
    public static Singleton getInstance() {
        return singleton;
    }
}

同步语句块

性能要求低,保证可行的最直接方法

/**
 * @author hanyl
 * @apiNote
 * @date 2024/3/5 11:49
 */
public class Singleton {

    /**
     * 利用静态变量来记录唯一的实例
     */
    private static Singleton singleton ;

    /**
     * 构造器声明为私有的,这样就只有在内部类才能够实例化
     */
    private Singleton() {
    }

    /**
     * 获取唯一的实例化对象
     *
     * @return
     */
    public static synchronized Singleton getInstance() {
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

命令模式 - Command

定义

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

案例分析

需求

现有一个远程控制系统,远程控制系统可以随意的接入不同的家电。远程控制系统可以接受一组命令。而按动不同的按钮,可以执行相应的家电命令。
还可以进行撤销,回退到上一步的操作。
例如,现在为其接上:台灯(开、关)、音响(开、关)、风扇(调高、调低、关闭)

分析

首先,我们需要一组家电。
Stereo.png
将“请求”封装成对象**:**
台灯开 、 台灯关、音响开、音响关、风扇调高、风扇调低、风扇关闭
为了进一步解耦,制作一个命令接口Command。而上述的命令都需要实现这个接口
Command.png
继续制作一个远程控制系统,远程控制系统需要去接口一组命令。
其中,远程控制系统:

  • 动态新增或者修改每一组的命令
  • 调用命令
  • 关闭命令
  • 撤销回退到上一步的操作

RemoteControl.png

如何实现撤销呢?可以这么做:

  • 新增一个实例变量:存储最后被调用的命令undoCommand
  • 之后,如果需要执行撤销方法,只需要再去获取这个命令,调用undo方法

为什么把命令变为“傻瓜式命令”?
这种操作解耦程度更高
如果需要设置一组宏命令呢?
不需要改变很多。依旧 implements Command,而实例变量则变为 一组命令对象 Command[] commands。

核心代码

/**
 * 风扇家电类
 * @author hanyl
 */
public class CeilingFan {
	String location = "";
	int level;
	public static final int HIGH = 2;
	public static final int MEDIUM = 1;
	public static final int LOW = 0;
 
	public CeilingFan(String location) {
		this.location = location;
	}
  
	public void high() {
		// turns the ceiling fan on to high
		level = HIGH;
		System.out.println(location + " ceiling fan is on high");
 
	} 

	public void medium() {
		// turns the ceiling fan on to medium
		level = MEDIUM;
		System.out.println(location + " ceiling fan is on medium");
	}

	public void low() {
		// turns the ceiling fan on to low
		level = LOW;
		System.out.println(location + " ceiling fan is on low");
	}
 
	public void off() {
		// turns the ceiling fan off
		level = 0;
		System.out.println(location + " ceiling fan is off");
	}
 
	public int getSpeed() {
		return level;
	}
}

public interface Command {

    public void execute();

    public void undo();
}

/**
 * @author hanyl
 * @apiNote 风扇调高命令
 * @date 2024/3/7 16:46
 */
public class CeilingFanHighCommand implements Command {

    CeilingFan ceilingFan;
    int prevSpeed;

    public CeilingFanHighCommand(CeilingFan ceilingFan) {
        this.ceilingFan = ceilingFan;
    }

    @Override
    public void execute() {
        prevSpeed = ceilingFan.getSpeed();
        ceilingFan.high();
    }

    @Override
    public void undo() {
        switch (prevSpeed){
            case CeilingFan.HIGH: 	ceilingFan.high(); break;
            case CeilingFan.MEDIUM: ceilingFan.medium(); break;
            case CeilingFan.LOW: 	ceilingFan.low(); break;
            default: 				ceilingFan.off(); break;
        }
    }
}

/**
 * @author hanyl
 * @apiNote 宏命令组
 * @date 2024/3/8 15:07
 */
public class MacroCommand implements Command{

    Command[] commands;

    public MacroCommand(Command[] commands) {
        this.commands = commands;
    }

    @Override
    public void execute() {
        for (int i = 0; i < commands.length; i++) {
            commands[i].execute();
        }
    }

    @Override
    public void undo() {
        for (int i = 0; i < commands.length; i++) {
            commands[i].undo();
        }
    }
}
/**
 * @author hanyl
 * @apiNote 远程控制器
 * @date 2024/3/7 16:28
 */
public class RemoteControl {

    Command[] onCommands;

    Command[] offCommands;

    Command undoCommand;

    public RemoteControl() {
        this.onCommands = new Command[7];
        this.offCommands = new Command[7];

        NoCommand noCommand = new NoCommand();
        for (int i = 0; i < 7; i++) {
            this.onCommands[i] = noCommand;
            this.offCommands[i] = noCommand;
        }
    }

    public void setCommandOns(int slot, Command commandOn, Command commandOff) {
        this.onCommands[slot] = commandOn;
        this.offCommands[slot] = commandOff;
    }

    public void onButtonWasPushed(int slot){
        this.onCommands[slot].execute();
        this.undoCommand = this.onCommands[slot];
    }

    public void offButtonWasPushed(int slot){
        this.offCommands[slot].execute();
        this.undoCommand = this.offCommands[slot];
    }

    public void undoButtonWasPushed() {
        undoCommand.undo();
    }

    @Override
    public String toString() {
        StringBuffer stringBuff = new StringBuffer();
        stringBuff.append("\n------ Remote Control -------\n");
        for (int i = 0; i < onCommands.length; i++) {
            stringBuff.append("[slot " + i + "] " + onCommands[i].getClass().getName()
                    + "    " + offCommands[i].getClass().getName() + "\n");
        }
        stringBuff.append("------ Remote Control -------\n");

        return stringBuff.toString();
    }

}

public class RemoteLoader {
    public static void main(String[] args) {

        RemoteControl remoteControl = new RemoteControl();

        CeilingFan ceilingFan = new CeilingFan("起居室");
        Command ceilingFanHighCommand = new CeilingFanHighCommand(ceilingFan);
        Command ceilingFanMediumCommand = new CeilingFanMediumCommand(ceilingFan);
        Command ceilingFanOffCommand = new CeilingFanOffCommand(ceilingFan);

        remoteControl.setCommandOns(0, ceilingFanMediumCommand, ceilingFanOffCommand);
        remoteControl.setCommandOns(1, ceilingFanHighCommand, ceilingFanOffCommand);

        remoteControl.onButtonWasPushed(0);
        remoteControl.offButtonWasPushed(0);
        remoteControl.undoButtonWasPushed();

        remoteControl.onButtonWasPushed(1);
        remoteControl.undoButtonWasPushed();
        System.out.print("//\n" +
                "// 宏命令组\n" +
                "//\n");


        Light light = new Light("起居室");
        Command lightCommandOn = new LightOnCommand(light);
        Command lightCommandOff = new LightOffCommand(light);

        Stereo stereo = new Stereo("起居室");
        Command stereoOffCommand = new StereoOffCommand(stereo);
        Command stereoOnCommand = new StereoOnCommand(stereo);

        Command[] firstCommands = {ceilingFanHighCommand, lightCommandOn, stereoOnCommand};
        Command[] secondCommands = {ceilingFanOffCommand, lightCommandOff, stereoOffCommand};

        Command macroCommand1 = new MacroCommand(firstCommands);
        Command macroCommand2 = new MacroCommand(secondCommands);

        RemoteControl control = new RemoteControl();
        control.setCommandOns(2, macroCommand1, macroCommand2);

        System.out.println("------------宏命令组:打开----------");
        control.onButtonWasPushed(2);
        System.out.println("------------宏命令组:关闭-----------");
        control.offButtonWasPushed(2);
        System.out.println("------------宏命令组:撤销------------");
        control.undoButtonWasPushed();

    }
}

总结

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

命令可以将运算块打包(一个接收者和一组动作)然后将它传来传去,就像是一般的对象一样。现在即使在命令对象被创建许久之后,运算依然可以被调用。事实上,它甚至可以在不同的线程中被调用。
具体应用:

  • 队列

某一端添加命令,然后另端则是线程。线程进行下面的动作:从队列中取出一个命令,调用它的execute()方法,等待这个调用完成:然后将此命令对象丢弃,再取出下一个命令……

  • 日志 / 事务

通过新增两个方法(store()、load()),命令模式能够够支持这一点。
执行命令的时候,就调用 store()
宕机的时候,就调用 load()

适配器模式 - Adapter

定义

适配器模式:将一个类的接口,转换成客户期望的另个接口。适配器让原本接口不兼容的类可以合作无间。(转化接口)

你可以写一个类,将新厂商接口转接成你所期望的接口
image.png

需求

实现火鸡接口类能够转为为鸭子接口类

核心代码

// 实现需要转化的类型接口
public class TurkeyAdapter implements Duck {

    Turkey turkey;

    // 获取适配器的引用
    public TurkeyAdapter(Turkey turkey) {
        this.turkey = turkey;
    }

    @Override
    public void quack() {
        // 由于 Turkey 不会 quack ,于是就转化为 gobble
        turkey.gobble();
    }

    @Override
    public void fly() {
        // 由于火鸡的飞翔距离很短,所以:连续调用来缩小两者飞翔距离的差距
        for (int i = 0; i < 5; i++) {
            turkey.fly();
        }

    }
}

测试类

public class DuckTestDrive {
    public static void main(String[] args) {
        // 绿头鸭
        Duck duck = new MallardDuck();

        // 火鸡
        Turkey turkey = new WildTurkey();

        // 伪装成鸭子的火鸡
        TurkeyAdapter turkeyAdapter = new TurkeyAdapter(turkey);

        System.out.println("The Turkey says...");
        turkey.gobble();
        turkey.fly();

        System.out.println("\nThe Duck says...");
        testDuck(duck);

        System.out.println("\nThe TurkeyAdapter says...");
        testDuck(turkeyAdapter);

    }

    static void testDuck(Duck duck) {
        duck.quack();
        duck.fly();
    }
    
    /**
     * @Result:
     * 
     * The Turkey says...
     * Gobble gobble
     * I'm flying a short distance
     *
     * The Duck says...
     * Quack
     * I'm flying
     *
     * The TurkeyAdapter says...
     * Gobble gobble
     * I'm flying a short distance
     * I'm flying a short distance
     * I'm flying a short distance
     * I'm flying a short distance
     * I'm flying a short distance
     *
     * 2024/3/13 20:04
     */
}

外观模式 - Facade

定义

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

核心代码

/**
 * 外观模式
 *
 * @author hanyl
 */
public class HomeTheaterFacade {
    // 以下实例变量为:类
	Amplifier amp;
	Tuner tuner;
	StreamingPlayer player;
	CdPlayer cd;
	Projector projector;
	TheaterLights lights;
	Screen screen;
	PopcornPopper popper;
 
	public HomeTheaterFacade(Amplifier amp,
                             Tuner tuner,
                             StreamingPlayer player,
                             Projector projector,
                             Screen screen,
                             TheaterLights lights,
                             PopcornPopper popper) {
 
		this.amp = amp;
		this.tuner = tuner;
		this.player = player;
		this.projector = projector;
		this.screen = screen;
		this.lights = lights;
		this.popper = popper;
	}
 
	public void watchMovie(String movie) {
		System.out.println("Get ready to watch a movie...");
		popper.on();
		popper.pop();
		lights.dim(10);
		screen.down();
		projector.on();
		projector.wideScreenMode();
		amp.on();
		amp.setStreamingPlayer(player);
		amp.setSurroundSound();
		amp.setVolume(5);
		player.on();
		player.play(movie);
	}
 
 
	public void endMovie() {
		System.out.println("Shutting movie theater down...");
		popper.off();
		lights.on();
		screen.up();
		projector.off();
		amp.off();
		player.stop();
		player.off();
	}

	public void listenToRadio(double frequency) {
		System.out.println("Tuning in the airwaves...");
		tuner.on();
		tuner.setFrequency(frequency);
		amp.on();
		amp.setVolume(5);
		amp.setTuner(tuner);
	}

	public void endRadio() {
		System.out.println("Shutting down the tuner...");
		tuner.off();
		amp.off();
	}
}

模板方法模式 - Template Method

定义

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

子类定义其中某些步骤的内容。在算法中的个别步骤可以有不同的实现细节,但是算法的结构依然维持不变
工厂方法是模板方法的一种特殊版本。
image.png

核心代码

public abstract class CaffeineBeverageWithHook {

    /**
     * 模板方法
     * 每个步骤都被一个方法代表了
     * 某些方法由超类处理
     * 某些方法由子类处理
     */
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        // 通过钩子函数,来确定是不是需要调用
        if (customerWantsCondiments()){
            addCondiments();
        }

    }

    // 必须需要由子类提供的方法,声明为抽象方法
    abstract void brew();

    abstract void addCondiments();

    // 这个具体的方法被定义在抽象类中将它声明为final,这样一来子类就无法覆盖它。
    // 它可以被模板方法直接使用或者被子类使用。
    final void boilWater() {
        System.out.println("Boiling water");
    }

    final void pourInCup() {
        System.out.println("Pouring into cup");
    }

    // 我们也可以有“默认不做事的方法”:hook 钩子函数
    // (可选性)子类可以视情况决定要不要覆盖它们
    boolean customerWantsCondiments() {
        return true;
    }

}

迭代器模式 - Iterator

定义

迭代器模式(iterator): 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
把游走的任务放在送代器上,而不是聚合上这样简化了聚合的接口和实现,也让责任各得其所。

image.png

核心代码

public class DinerMenuIterator implements Iterator<MenuItem> {
    MenuItem[] menuItems;
    int position = 0;

    public DinerMenuIterator(MenuItem[] menuItems) {
        this.menuItems = menuItems;
    }

    @Override
    public boolean hasNext() {
        return menuItems.length > position;
    }

    @Override
    public MenuItem next() {
        return menuItems[position++];
    }

    @Override
    public void remove() {
        if (position <= 0) {
            throw new IllegalStateException
                    ("You can't remove an item until you've done at least one next()");
        }
        // 因为使用的是固定长度的数组,所以在remove()被调用时,我们将后面的所有元素往前移动一个位置。
        if (menuItems[position-1] != null) {
            for (int i = position-1; i < (menuItems.length-1); i++) {
                menuItems[i] = menuItems[i+1];
            }
            menuItems[menuItems.length-1] = null;
        }
    }
}

组合模式 - Composite

定义

组合模式(composite):组合模式–允许你将对象组成树形结构来表现“整体/部分”的层次结构。组合能让客户以一致的方式处理个别对象和对象组合。
有数个对象的集合,它们彼此之间有“整体/部分”的关系,并且你想用一致的方式对待这些对象时,你就需要我。
组合模式允许我们像对待单个对象一样对待对象集合。
包含其他组件的组件为组合对象
而没有包含其他组件的组件为叶节点对象

核心代码

MenuComponent.png

import java.util.Iterator;

public abstract class MenuComponent {
    
	// 组合节点的组合方法
	public void add(MenuComponent menuComponent) {
		throw new UnsupportedOperationException();
	}
	public void remove(MenuComponent menuComponent) {
		throw new UnsupportedOperationException();
	}
	public MenuComponent getChild(int i) {
		throw new UnsupportedOperationException();
	}
  
	// 叶子节点的操作方法
	public String getName() {
		throw new UnsupportedOperationException();
	}
	public String getDescription() {
		throw new UnsupportedOperationException();
	}
	public double getPrice() {
		throw new UnsupportedOperationException();
	}
	public boolean isVegetarian() {
		throw new UnsupportedOperationException();
	}

	// 由子类具体实现
	public abstract Iterator<MenuComponent> createIterator();
 
	// 组合节点 和叶子节点均可重写
	public void print() {
		throw new UnsupportedOperationException();
	}
}
import java.util.ArrayList;
import java.util.Iterator;

public class Menu extends MenuComponent {
	Iterator<MenuComponent> iterator = null;
	ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>();
	String name;
	String description;
  
	public Menu(String name, String description) {
		this.name = name;
		this.description = description;
	}
 
	@Override
	public void add(MenuComponent menuComponent) {
		menuComponents.add(menuComponent);
	}
 
	@Override
	public void remove(MenuComponent menuComponent) {
		menuComponents.remove(menuComponent);
	}
 
	@Override
	public MenuComponent getChild(int i) {
		return menuComponents.get(i);
	}
 
	@Override
	public String getName() {
		return name;
	}
 
	@Override
	public String getDescription() {
		return description;
	}

  
	@Override
	public Iterator<MenuComponent> createIterator() {
		if (iterator == null) {
			iterator = new CompositeIterator(menuComponents.iterator());
		}
		return iterator;
	}
 
 
	@Override
	public void print() {
		System.out.print("\n" + getName());
		System.out.println(", " + getDescription());
		System.out.println("---------------------");
  
		Iterator<MenuComponent> iterator = menuComponents.iterator();
		while (iterator.hasNext()) {
			MenuComponent menuComponent = iterator.next();
			menuComponent.print();
		}
	}
}

public class MenuItem extends MenuComponent {
 
	String name;
	String description;
	boolean vegetarian;
	double price;
    
	public MenuItem(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 double getPrice() {
		return price;
	}
  
	@Override
	public boolean isVegetarian() {
		return vegetarian;
	}

	@Override
	public Iterator<MenuComponent> createIterator() {
		return new NullIterator();
	}
 
	@Override
	public void print() {
		System.out.print("  " + getName());
		if (isVegetarian()) {
			System.out.print("(v)");
		}
		System.out.println(", " + getPrice());
		System.out.println("     -- " + getDescription());
	}

}
import java.util.Iterator;
import java.util.Stack;

public class CompositeIterator implements Iterator<MenuComponent> {
	Stack<Iterator<MenuComponent>> stack = new Stack<Iterator<MenuComponent>>();
   
	public CompositeIterator(Iterator<MenuComponent> iterator) {
		stack.push(iterator);
	}
   
	@Override
	public MenuComponent next() {
		if (hasNext()) {
			Iterator<MenuComponent> iterator = stack.peek();
			MenuComponent component = iterator.next();
			stack.push(component.createIterator());
			return component;
		} else {
			return null;
		}
	}
  
	@Override
	public boolean hasNext() {
		if (stack.empty()) {
			return false;
		} else {
			Iterator<MenuComponent> iterator = stack.peek();
			if (!iterator.hasNext()) {
				stack.pop();
				return hasNext();
			} else {
				return true;
			}
		}
	}
	
	/*
	 * No longer needed as of Java 8
	 * 
	 * (non-Javadoc)
	 * @see java.util.Iterator#remove()
	 *
	public void remove() {
		throw new UnsupportedOperationException();
	}
	*/
}
/*
 * 解决某些
 */
public class NullIterator implements Iterator<MenuComponent> {
   
	public MenuComponent next() {
		return null;
	}
  
	public boolean hasNext() {
		return false;
	}
   
	/*
	 * No longer needed as of Java 8
	 * 
	 * (non-Javadoc)
	 * @see java.util.Iterator#remove()
	 * 
	public void remove() {
		throw new UnsupportedOperationException();
	}
	*/
}

这样,我们在使用调用的时候,就可以统一:

public class Waitress {
	MenuComponent allMenus;
 
	public Waitress(MenuComponent allMenus) {
		this.allMenus = allMenus;
	}
 
	public void printMenu() {
		allMenus.print();
	}
  
	public void printVegetarianMenu() {
		Iterator<MenuComponent> iterator = allMenus.createIterator();

		System.out.println("\nVEGETARIAN MENU\n----");
		while (iterator.hasNext()) {
			MenuComponent menuComponent = iterator.next();
			try {
				if (menuComponent.isVegetarian()) {
					menuComponent.print();
				}
			} catch (UnsupportedOperationException e) {}
		}
	}
}

状态模式 - State

定义

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

状态图:
image.png
状态图转化为代码:
image.png
类图
image.png

核心代码

接口类或者抽象类,高度抽象状态

public interface State {

	/**
	 * 付款
	 */
	public void insertQuarter();

	/**
	 * 退款
	 */
	public void ejectQuarter();

	/**
	 * 转动曲柄
	 */
	public void turnCrank();

	/**
	 * 发放糖果
	 */
	public void dispense();

	/**
	 * 重新填入糖果
	 */
	public void refill();
}

用一个Context标识状态之间的流转

public class GumballMachine {
 
	State soldOutState;
	State noQuarterState;
	State hasQuarterState;
	State soldState;
	State winnerState;
 
	State state = soldOutState;
	int count = 0;
 
	public GumballMachine(int numberGumballs) {
		soldOutState = new SoldOutState(this);
		noQuarterState = new NoQuarterState(this);
		hasQuarterState = new HasQuarterState(this);
		soldState = new SoldState(this);
		winnerState = new WinnerState(this);

		this.count = numberGumballs;
 		if (numberGumballs > 0) {
			state = noQuarterState;
		} 
	}
	/**
	 * 付款
	 */
	public void insertQuarter() {
		state.insertQuarter();
	}
	/**
	 * 退款
	 */
	public void ejectQuarter() {
		state.ejectQuarter();
	}
	/**
	 * 转动曲柄
	 */
	public void turnCrank() {
		state.turnCrank();
		state.dispense();
	}

	// 辅助方法
	/**
	 * 释放糖果
	 */
	void releaseBall() {
		System.out.println("A gumball comes rolling out the slot...");
		if (count > 0) {
			count = count - 1;
		}
	}
	/**
	 * 重复投币
	 */
	void refill(int count) {
		this.count += count;
		System.out.println("The gumball machine was just refilled; its new count is: " + this.count);
		state.refill();
	}

	void setState(State state) {
		this.state = state;
	}

 
	int getCount() {
		return count;
	}

    public State getState() {
        return state;
    }

    public State getSoldOutState() {
        return soldOutState;
    }

    public State getNoQuarterState() {
        return noQuarterState;
    }

    public State getHasQuarterState() {
        return hasQuarterState;
    }

    public State getSoldState() {
        return soldState;
    }

    public State getWinnerState() {
        return winnerState;
    }
 
	@Override
	public String toString() {
		StringBuffer result = new StringBuffer();
		result.append("\nMighty Gumball, Inc.");
		result.append("\nJava-enabled Standing Gumball Model #2004");
		result.append("\nInventory: " + count + " gumball");
		if (count != 1) {
			result.append("s");
		}
		result.append("\n");
		result.append("Machine is " + state + "\n");
		return result.toString();
	}
}

以某个状态举例

public class HasQuarterState implements State {
	Random randomWinner = new Random(System.currentTimeMillis());
	GumballMachine gumballMachine;
 
	public HasQuarterState(GumballMachine gumballMachine) {
		this.gumballMachine = gumballMachine;
	}
	/**
	 * 付款
	 */
	@Override
	public void insertQuarter() {
		System.out.println("You can't insert another quarter");
	}
	/**
	 * 退款
	 */
	@Override
	public void ejectQuarter() {
		System.out.println("Quarter returned");
		gumballMachine.setState(gumballMachine.getNoQuarterState());
	}
	/**
	 * 转动曲柄
	 */
	@Override
	public void turnCrank() {
		System.out.println("You turned...");
		int winner = randomWinner.nextInt(10);
		if ((winner == 0) && (gumballMachine.getCount() > 1)) {
			gumballMachine.setState(gumballMachine.getWinnerState());
		} else {
			gumballMachine.setState(gumballMachine.getSoldState());
		}
	}
	/**
	 * 发放糖果
	 */
    @Override
	public void dispense() {
        System.out.println("No gumball dispensed");
    }
	/**
	 * 重新填入糖果
	 */
    @Override
	public void refill() { }
 
	@Override
	public String toString() {
		return "waiting for turn of crank";
	}
}

代理模式 - Proxy

房产中介、经纪人等等

定义

代理模式(proxy):控制对象的访问,对另一个对象提供一个替身或占位符以访问这个对象。
远程对象交互通讯,访问开销大的对象,控制对象的访问… …

需求

举例:约会服务对象系统,要求自己能够修改自己的基础信息;评分只能由他人评分
——使用Java的动态代理实现保护代理
要修正这些问题,你必须创建两个代理:

  • 一个访问你自己的PersonBean对象
  • 另一个访问另一顾客的PersonBean对象

这样,代理就可以控制在每一种情况下允许哪一种请求

核心代码

public class NonOwnerInvocationHandler implements InvocationHandler { 
	Person person;
 
	public NonOwnerInvocationHandler(Person person) {
		this.person = person;
	}
 
	public Object invoke(Object proxy, Method method, Object[] args) 
			throws IllegalAccessException {
		// nonOwnerProxy.setGeekRating(3);
		// method.getName() : setGeekRating
		// method.invoke((person, args):其中,person被代理的对象是 Person, args参数为 10
		// 也就是说,实际上是调用了 Person 的 setGeekRating,里面的参数为 10
		try {
			// 它的getName()方法,我们就可以知道proxy被调用的方法是什么。
			if (method.getName().startsWith("get")) {
				// 原始proxy被调用的方法。这个对象在调用时被传给我们。只不过加载调用的是真正的主题(person)
				return method.invoke(person, args);
   			} else if (method.getName().equals("setGeekRating")) {
				return method.invoke(person, args);
			} else if (method.getName().startsWith("set")) {
				throw new IllegalAccessException();
			} 
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } 
		return null;
	}
}
System.out.println("--------------------------------------------------------------");
// 获取代理对象
// Proxy本身是利用静态的Proxy.newProxyInstance()方法在运行时动态地创建的。
Person nonOwnerProxy = (Person) Proxy.newProxyInstance(
    joe.getClass().getClassLoader(),
    joe.getClass().getInterfaces(),
    new NonOwnerInvocationHandler(joe));

System.out.println("名称:" + nonOwnerProxy.getName()+ "\n" + joe);
System.out.println("【非法操作开始】");
try {
    System.out.println("Q:非拥有者代理对象设置属性:Interests?");
    nonOwnerProxy.setInterests("保龄球, 高尔夫");
} catch (Exception e) {
    System.out.println("A:非拥有者代理对象设置属性:Interests —— 失败");
}
System.out.println("【非法操作结束】");
System.out.println("--------------------------------------------------------------");
System.out.println("名称:" + nonOwnerProxy.getName()+ "\n" + joe);
System.out.println("【合法操作开始】");
System.out.println("Q:非拥有者代理尝试对象设置属性:Rating?");
nonOwnerProxy.setGeekRating(3);
System.out.println("A:非拥有者代理尝试对象设置属性:Rating —— 成功");
System.out.println("【合法操作结束】");
System.out.println("当前的Rating是:" + nonOwnerProxy.getGeekRating());
System.out.println("--------------------------------------------------------------");

总结

思维导图

模式的分类

根据模式的目标分类

创建型

涉及到将对象实例化。这类模式都提供一个方法,将客户从所需要实例化的对象中解耦出来。

  • 单例模式
  • 抽象工厂模式
  • 工厂方法模式
  • Prototype
  • Builfer
行为型

只要是行为型模式,都涉及到类和对象如何交互及分配职责

  • 模板方法模式
  • 命令模式
  • 迭代器模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • Chain of Responsibility
  • Visitor
  • Mediator
  • Memento
  • Interpreter
结构型

结构型模式可以让你把类或对象组合到更大的结构中。

  • 装饰器模式
  • 代理模式
  • 组合模式
  • 外观模式
  • 适配器模式
  • Flyweight
  • Bridge

模式所处理的对象

  • 类模式描述类之间的关系如何通过继承定义。
  • 类模式的关系是在编译时建立的。
  • 模板方法模式
  • 工厂方法模式
  • 适配器模式
  • Interpreter
对象
  • 对象模式描述对象之间的关系,而且主要是利用组合定义。
  • 对象模式的关系通常在运行时建立,而且更加动态、更有弹性
  • 组合模式
  • 装饰器模式
  • 代理模式
  • 策略模式
  • 抽象工厂模式
  • 单例模式
  • 状态模式
  • 观察者模式
  • 外观模式
  • 命令模式
  • 迭代器模式
  • Builder
  • Bridge
  • Flyweight
  • Prototype
  • Chain of Responsibility
  • Mediator
  • Memento
  • Visitor

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/531456.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

素人小红书发布如何选择账号?

如何从众多账号中筛选出符合品牌或产品特性、具有高性价比和合作潜力的账号&#xff0c;成为了许多品牌和营销人士关注的焦点。素人小红书发布如何选择账号&#xff1f;接下来伯乐网络传媒就来给大家分享一下&#xff0c;希望能为你在小红书上进行账号选择提供一些有价值的参考…

docker部署postgresql数据库和整合springboot连接数据源

公司想要把部分sqlserver的旧服务迁移到PG数据库&#xff0c;先写一个示例的demo&#xff0c;需要用docker部署postgresql数据库和整合springboot连接数据源 安装 下载最新镜像 docker pull postgres创建并且启动容器 docker run -it --name postgres --restart always -e …

嵌入式应会的模电数电基础

AC/DC交直流 电压 欧姆定律 常见元器件 电阻器 并联电阻&#xff0c;增加通路&#xff0c;电阻更小&#xff0c;电流更大 串联电阻&#xff0c;电阻更大&#xff0c;电流越小 相同阻值的电阻&#xff0c;个头大小不同主要区别在功率容量、耐压能力和散热性能方面。 功率容量…

【STL】priority_queue的底层原理及其实现

文章目录 priority_queue的介绍库中priority_queue的使用什么叫仿函数&#xff1f; 模拟实现prioprity_queue类 priority_queue的介绍 解释以上内容 priority_queue&#xff08;优先级队列&#xff09;跟stack、queue一样&#xff0c;都是一种容器适配器&#xff0c;根据严格的…

SpringBoot中定时任务踩坑,@Scheduled重复执行问题排查(看完直接破防)

前言 今天再开发业务需求的过程中&#xff0c;需要用到定时任务&#xff0c;原本定的是每10分钟推送一次&#xff0c;可是当每次十分钟到的时候&#xff0c;定时任务就会推送多条&#xff01;但是非常奇怪的是&#xff0c;本地调试的时候不会有问题&#xff0c;只有当你部署到…

OpenCV | 图像读取与显示

OpenCV 对图像进行处理时&#xff0c;常用API如下&#xff1a; API描述cv.imread根据给定的磁盘路径加载对应的图像&#xff0c;默认使用BGR方式加载cv.imshow展示图像cv.imwrite将图像保存到磁盘中cv.waitKey暂停一段时间&#xff0c;接受键盘输出后&#xff0c;继续执行程序…

windows 之 redis非安装版,启动与初始化密码

1、下载redis 免安装版 2、解压后&#xff0c;启动服务 3、双击客服端 4、设置密码 config set requirepass root123456成功后&#xff0c;退出服务再次双击 5、登录 再次执行命名时已经没权限了 使用 auth password 登录 成功后&#xff0c;就可以了 auth root123456 …

arcgis使用面shp文件裁剪线shp文件报错

水系数据裁剪&#xff0c;输出为空&#xff1a; ArcGIS必会的几个工具的应用 --提取、分割、融合、裁剪&#xff08;矢&#xff09;、合并、追加、镶嵌、裁剪&#xff08;栅&#xff09;、重采样_arcgis分割-CSDN博客 下面的方法都不行&#xff1a; ArcGIS Clip&#xff08;裁…

JavaScript - 你遇到过哪几种Javascript的错误类型

难度级别:中级及以上 提问概率:50% 我们在开发Javascript代码的时候,经常一不小心就会遇到各种各样的异常,浏览器也会及时给出错误信息,那么一般会遇到哪几种异常情况呢,我们来看一下。 1 ReferenceError错误 ReferenceError几乎是最…

Ubuntu 20.04.06 PCL C++学习记录(二十四)

[TOC]PCL中点云分割模块的学习 学习背景 参考书籍&#xff1a;《点云库PCL从入门到精通》以及官方代码PCL官方代码链接,&#xff0c;PCL版本为1.10.0&#xff0c;CMake版本为3.16&#xff0c;可用点云下载地址 学习内容 如何使用已知系数的 SAC_Models 从点云中提取参数模型…

Tomcat 获取客户端真实IP X-Forwarded-For

Tomcat 获取客户端真实IP X-Forwarded-For 代码实现&#xff1a; 在Host标签下面添加代码&#xff1a; <Valve className"org.apache.catalina.valves.RemoteIpValve" remoteIpHeader"x-forwarded-for" remoteIpProxiesHeader"x-forwarded-by&q…

面试算法-165-随机链表的复制

题目 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点的值。新节…

网络安全---非对称数据加密签名验证

一、课题描述 三位同学一组完成数据的非对称加密和数字签名验证传输。 三位同学分别扮演图中 Alice、Bob 和 CA 三个角色&#xff0c;Bob 和 Alice 从 CA 中获得数字证书、Bob 向 Alice 发送秘密发送一段加密并签名后的信息&#xff0c;Alice 获取 Bob 发送的加密信息&#x…

关于ARM的一些问题

一&#xff0c;arm的工作模式有哪些&#xff1f; User&#xff1a;非特权模式 FIQ&#xff1a;高优先级中断进入 IRQ&#xff1a;低优先级中断进入 Supervisor:当复位或软中断指令进入 Abort: 当存取异常时 Undef:当执行未定义指令时会进入这种模式 System:使用和User模式相同…

科技云报道:从“奇点”到“大爆炸”,生成式AI开启“十年周期”

科技云报道原创。 世界是复杂的&#xff0c;没有人知道未来会怎样&#xff0c;但如果单纯从技术的角度&#xff0c;我们总是能够沿着技术发展的路径&#xff0c;找到一些主导未来趋势的脉络。 从Sora到Suno&#xff0c;从OpenAI到Copilot、Blackwell&#xff0c;这些热词在大…

【Redis】底层跳表实现

先巩固Redis的数据类型以及底层的数据结构&#xff1a; ZSet&#xff08;有序集合&#xff09;可以使用两种不同的内部数据结构来表示&#xff1a;压缩列表&#xff08;ziplist&#xff09;和跳跃表&#xff08;skiplist&#xff09;。 跳表是redis底层SortedSet(ZSet)的数据…

JAVA并发编程(二)_线程池

JAVA线程池 1.1Java 线程池之 Executor 框架 为了实现线程池和管理线程池&#xff0c;JDK 给我们提供了基于 Executor 接口的一系列接口、抽象类、实现类&#xff0c;我们把它称作线程池的 Executor 框架&#xff0c;Executor 框架本质上是一个线程池&#xff1b; ​ Java 线…

Linux LVM磁盘扩容

1、查看磁盘情况 df -h df -h2、查看逻辑卷 lvdisplay lvdisplay3、查看逻辑组 vgdisplay vgdisplay4、查看物理卷 pvdisplay pvdisplay5、查看磁盘 fdisk -l fdisk -l6、磁盘分区fdisk /dev/磁盘名 # 上一步查看到的新硬盘路径 fdisk /dev/vdb7、格式化磁盘mkfs -t ext4…

【负载均衡——一致性哈希算法】

1.一致性哈希是什么 一致性哈希算法就很好地解决了分布式系统在扩容或者缩容时&#xff0c;发生过多的数据迁移的问题。 一致哈希算法也用了取模运算&#xff0c;但与哈希算法不同的是&#xff0c;哈希算法是对节点的数量进行取模运算&#xff0c;而一致哈希算法是对 2^32 进…

基于SSM+Jsp+Mysql的超市管理系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…