【设计模式】笔记篇

news2024/11/24 20:38:38

目录标题

  • 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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++基础】变量和数据类型

变量和数据类型 引言&#xff1a;为什么需要变量&#xff1f;一、变量1. 变量的声明2. 变量的赋值3. 标识符4. 作用域5. 常量6. 注意 二、基本数据类型1. 整型1.1 常用整型1.2 无符号整型1.3 char类型1.4 bool类型 2. 浮点型3. 字面值常量3.1 整型字面值3.2 浮点型字面值3.3 字…

蓝桥杯第十二届c++大学B组详解

目录 1.空间 2.直线 3.路径 4.卡片 5.货物摆放 6.时间显示 7.砝码称重 8.杨辉三角 9.双向排序 10.括号序列 1.空间 题目解析&#xff1a;1Byte 8bit 1kb 1024B 1MB 1024kb; 先将256MB变成Byte 256 * 1024 * 1024; 再将32位 变成Byte就是 32 / 8 4&#xff1b;…

人工智能前沿成科技竞争新高地

以下文章来源&#xff1a;经济参考报 近日&#xff0c;首届中国具身智能大会&#xff08;CEAI 2024&#xff09;在上海举行。作为人工智能领域的前沿热点&#xff0c;具身智能正逐步走进现实&#xff0c;成为当前全球科技竞争的新高地、未来产业的新赛道、经济发展的新引擎。 “…

在Linux终端查找指定类型的文件并统计数量

下面举例说明&#xff1a; find /path/to/directory -type f -exec file {} \; | grep "MIDI"它的作用是在指定的目录&#xff08;/path/to/directory&#xff09;中搜索所有的文件&#xff08;-type f&#xff09;&#xff0c;然后使用file命令检查每个文件的类型&a…

《QT实用小工具·二十四》各种数学和数据的坐标演示图

1、概述 源码放在文章末尾 该项目实现了各种数学和数据的坐标演示图&#xff0c;下面是demo演示&#xff1a; 项目部分代码如下&#xff1a; #ifndef FRMMAIN_H #define FRMMAIN_H#include <QWidget> class QAbstractButton;namespace Ui { class frmMain; }class fr…

AWD靶机实战第二天:使用python脚本获取flag

上一部分我们通过读源码和利用源码审计的工具找到了这个漏洞&#xff0c;但是在比赛的时候有很多靶机&#xff0c;我们去一个个的注册&#xff0c;一个个的登录然后输入很浪费时间&#xff0c;所以我选择写一个python脚本来实现自动获得flag以及自动提交flag. 首先我们将这次的…

群联AI云防护中的防盗链技术原理及其作用探析---

一、引言 随着云计算和AI技术的快速发展&#xff0c;云防护方案已经成为现代企业防范网络攻击和保护数字资产的重要手段之一。群联科技作为存储解决方案和技术服务的领导者&#xff0c;已将其AI技术应用于云端防护系统中&#xff0c;并特别强化了防盗链功能&#xff0c;以帮助…

第十四届蓝桥杯省赛真题-幸运数

代码及解析: #include <iostream> using namespace std;bool check(int x) {int tmpx,len0;while(tmp){len;tmp/10;}//算位数的方法 if(len%21)return 0;//直接retrun 0 int tlen/2;int ans10,ans20;while(t--){ans1x%10;//加每一位x/10; }while(x){ans2x%10;x/10;}if(…

什么是SSL重签(reissue)?具体怎么做?

SSL重签&#xff08;reissue&#xff09;是指在SSL/TLS证书到期或需要更新时&#xff0c;证书持有者向证书颁发机构&#xff08;CA&#xff09;申请新的证书的过程。这通常是因为原有证书的有效期即将结束&#xff0c;或者证书因为某些原因&#xff08;如密钥泄露、证书损坏等&…

计算机视觉——基于深度学习UNet实现的复杂背景文档二值化算法实现与模型训练

1. 引言 阈值分割可以被视为一个分类问题&#xff0c;通常涉及两个类别&#xff0c;这也是为什么阈值分割也被称为二值化。对于文档图像&#xff0c;我们期望阈值算法能够正确地将墨水分类为黑色&#xff0c;将纸张分类为白色&#xff0c;从而得到二值化图像。对于数字灰度图像…

自动驾驶硬件系统-激光雷达(Lidar)测量模型

自动驾驶硬件系统-激光雷达(Lidar)测量模型 激光雷达(Lidar, Light Detection And Ranging)是Google系自动驾驶技术路线广泛应用的硬件传感器。 附赠自动驾驶学习资料和量产经验&#xff1a;链接 1、激光雷达(Lidar)的工作原理 通过持续不断的发射激光束&#xff0c;激光束遇…

pytest常用钩子函数

1、什么叫钩子函数 在Pytest框架中&#xff0c;钩子函数是一种允许用户扩展或者自定义测试执行过程的机制。钩子函数允许用户在测试的不同阶段插入自定义的代码&#xff0c;以实现特定的行为&#xff0c;操作或处理。这种插入式的机制使得Pytest具有高度的灵活性和扩展性。 如…

2024热门电商项目,“视频号/蝴蝶号小店”必须排在首位!

大家好&#xff0c;我是电商小布。 在如今这个流量为王的时代&#xff0c;电商行业的崛起也是非常迅速&#xff0c;且占据了各大行业的头部位置。 借助这个优势&#xff0c;也产生了各种各样的电商项目。 如果你问我在2024年&#xff0c;有什么必须考虑的创业项目&#xff0…

JR-SMD201网络直播解码器

详细介绍&#xff1a; JR-SMD201网络直播解码器&#xff0c;支持AVS/H.265/H.264/MPEG2解码&#xff0c;支持IP输入&#xff0c;支持1080P/1080I/720P/576I/480I多种分辨率&#xff0c;支持DRA/AC3/EAC3/AAC/MPEG等音频。 产品特点 支持多种输入方式IP 接口丰富&#xff0c;CV…

双链表的操作

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 每一个裂缝都是为透出光而努力&#…

【记录】LangChain|Ollama结合LangChain使用的速通版(包含代码以及切换各种模型的方式)

官方教程非常长&#xff0c;我看了很认可&#xff0c;但是看完了之后呢就需要一些整理得当的笔记让我自己能更快地找到需求。所以有了这篇文章。【写给自己看的&#xff0c;里面半句废话的解释都没有&#xff0c;如果看不懂的话直接看官方教程再看我的】 我是不打算一开始就用…

计算机进制

进制 进制也就是进位制&#xff0c;是人们规定的一种进位方法对于任何一种进制—X进制&#xff0c;就表示某一位置上的数运算时是逢X进一位 十进制是逢十进一&#xff0c;十六进制是逢十六进一&#xff0c;二进制就是逢二进一&#xff0c;以此类推&#xff0c;x进制就是逢x进…

【汇编语言实战】统计个数

已知10个分布在0至100内的正整数&#xff0c;统计大于等于60的数的个数和小于60的数的个数 C语言描述该程序流程&#xff1a; #include <stdio.h> int main() {int arr1[]{11,33,73,52,93,84,67,56,64,75};int num10;for(int i1;i<10;i){if(arr1[i]>60){num1;}}p…

关于帆软报表取OA选择框-下拉列表值的解决方案

问题&#xff1a;制度一览表每次在OA端增加或修改制度类别后&#xff0c;都要在帆软报表修改后才能正确显示。 原因分析&#xff1a;以前大家没有找到OA下拉列表的维护表&#xff0c;采用的是在帆软端通过内置数据集来解决&#xff0c;需要人工维护。 解决方案&#xff1a; …

删除有序链表中重复的元素 【C语言】

参考&#xff1a;https://blog.csdn.net/ABABC1234/article/details/131329837 删除有序链表中重复的元素&#xff0c;比如 输入链表1&#xff0c;2&#xff0c;2&#xff0c;3&#xff0c;3&#xff0c;4。输出链表1&#xff0c;4 输入链表1&#xff0c;1&#xff0c;2&#…