【设计模式】笔记篇

目录标题

  • 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();}@Overridepublic 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 的方法重写:接受推送过来的信息*/@Overridepublic void update(Observable observable, Object arg) {if (observable instanceof WeatherData) {WeatherData weatherData = (WeatherData)observable;this.temperature = weatherData.getTemperature();this.humidity = weatherData.getHumidity();display();}}/*** DisplayElement 的方法重写*/@Overridepublic 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";}@Overridepublic double cost() {return .99;}
}
/*** 【抽象装饰者】必须让Condiment Decorator能* 够取代Beverage,所以将Condiment* Decorator扩展自 Beverage 类。*/
public abstract class CondimentDecorator extends Beverage {Beverage beverage;/*** 所有的调料装饰者都必须重新实现getDescription()方法。* @return*/@Overridepublic abstract String getDescription();
}
/*** @author hanyl* @apiNote 豆浆 */
public class Soy extends CondimentDecorator {/*** 用个实例变量用来记录被装饰者* @param beverage 被装饰者*/public Soy(Beverage beverage) {this.beverage = beverage;}/*** 完整的输出每个被装饰者的每个信息,* 而不是仅仅描述当前的调料信息* * @return*/@Overridepublic String getDescription() {// 1. 先利用委托,得到一个描述信息// 2. 再进行附加return beverage.getDescription() + ", Soy";}@Overridepublic 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;}@Overridepublic 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*/@OverridePizza 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;}@Overridepublic 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 薄皮面团*/@Overridepublic Dough createDough() {return new ThinCrustDough();}/*** 制作调味酱** @return Sauce 蕃茄酱*/@Overridepublic Sauce createSauce() {return new MarinaraSauce();}/*** 制作奶酪* @return Cheese 雷吉亚诺奶酪*/@Overridepublic Cheese createCheese() {return new ReggianoCheese();}/*** 蔬菜搭配* @return Veggies[] 蔬菜数据合集:大蒜、洋葱*/@Overridepublic Veggies[] createVeggies() {Veggies veggies[] = { new Garlic(), new Onion() };return veggies;}/*** 搭配香肠** @return Pepperoni 薄片香肠*/@Overridepublic Pepperoni createPepperoni() {return new SlicedPepperoni();}/*** 制作蛤蜊** @return Clams 新鲜蛤蜊*/@Overridepublic Clams createClam() {return new FreshClams();}
}
/*** 奶酪披萨* @author hanyl*/
public class CheesePizza extends Pizza{PizzaIngredientFactory ingredientFactory;/*** 通过不同的地区的原料工厂制作奶酪披萨* @param ingredientFactory 不同的地区的原料工厂*/public CheesePizza(PizzaIngredientFactory ingredientFactory) {this.ingredientFactory = ingredientFactory;}/*** 奶酪披萨:制作面团、抹上酱料、铺上奶酪*/@Overridepublic 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 {@Overrideprotected 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 highlevel = HIGH;System.out.println(location + " ceiling fan is on high");} public void medium() {// turns the ceiling fan on to mediumlevel = MEDIUM;System.out.println(location + " ceiling fan is on medium");}public void low() {// turns the ceiling fan on to lowlevel = LOW;System.out.println(location + " ceiling fan is on low");}public void off() {// turns the ceiling fan offlevel = 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;}@Overridepublic void execute() {prevSpeed = ceilingFan.getSpeed();ceilingFan.high();}@Overridepublic 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;}@Overridepublic void execute() {for (int i = 0; i < commands.length; i++) {commands[i].execute();}}@Overridepublic 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();}@Overridepublic 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;}@Overridepublic void quack() {// 由于 Turkey 不会 quack ,于是就转化为 gobbleturkey.gobble();}@Overridepublic 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;}@Overridepublic boolean hasNext() {return menuItems.length > position;}@Overridepublic MenuItem next() {return menuItems[position++];}@Overridepublic 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;}@Overridepublic void add(MenuComponent menuComponent) {menuComponents.add(menuComponent);}@Overridepublic void remove(MenuComponent menuComponent) {menuComponents.remove(menuComponent);}@Overridepublic MenuComponent getChild(int i) {return menuComponents.get(i);}@Overridepublic String getName() {return name;}@Overridepublic String getDescription() {return description;}@Overridepublic Iterator<MenuComponent> createIterator() {if (iterator == null) {iterator = new CompositeIterator(menuComponents.iterator());}return iterator;}@Overridepublic 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;}@Overridepublic String getName() {return name;}@Overridepublic String getDescription() {return description;}@Overridepublic double getPrice() {return price;}@Overridepublic boolean isVegetarian() {return vegetarian;}@Overridepublic Iterator<MenuComponent> createIterator() {return new NullIterator();}@Overridepublic 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);}@Overridepublic MenuComponent next() {if (hasNext()) {Iterator<MenuComponent> iterator = stack.peek();MenuComponent component = iterator.next();stack.push(component.createIterator());return component;} else {return null;}}@Overridepublic 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;}@Overridepublic 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;}/*** 付款*/@Overridepublic void insertQuarter() {System.out.println("You can't insert another quarter");}/*** 退款*/@Overridepublic void ejectQuarter() {System.out.println("Quarter returned");gumballMachine.setState(gumballMachine.getNoQuarterState());}/*** 转动曲柄*/@Overridepublic 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());}}/*** 发放糖果*/@Overridepublic void dispense() {System.out.println("No gumball dispensed");}/*** 重新填入糖果*/@Overridepublic void refill() { }@Overridepublic 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,里面的参数为 10try {// 它的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.mzph.cn/news/805084.shtml

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

相关文章

突如其来:OpenAI分家的Anthropic公司悄悄地释放出他们的秘密武器——Claude3

突如其来的消息&#xff0c;OpenAI分家的Anthropic公司悄悄地释放出他们的秘密武器——Claude3 这货居然在默默无闻中一举超越了GPT-4的地位。没发布会&#xff0c;没吹牛逼&#xff0c;就发了一帖子。 字少&#xff0c;事大。 Claude3独挡一面的推理能力 Anthropic推出了三款…

【springboot开发】Gradle VS Maven

前言&#xff1a; java构建工具的主要作用是依赖管理和构建的生命周期管理。gradle和maven是目前java中最流行的两个构建工具&#xff0c;springboot常用maven&#xff0c;Android studio使用gradle。 目录 1. 简介2. Maven2.1 安装2.2 依赖管理2.3 构建生命周期管理 3. Gradle…

【noVNC】使用noVNC实现浏览器网页访问vnc(基于web的远程桌面)

1.VNC本身提供的http连接方式&#xff0c;可传输文件&#xff0c;画面有卡顿&#xff0c;需要安装jre 2.noVNC访问方式&#xff0c;不可传输文件&#xff0c;画面较为流畅&#xff0c;不用安装插件运行环境 一、noVNC 是什么 Web 端的Vnc软件&#xff0c;通过noVNC&#xff0…

tsc --init 报错

运行 tsc --init 报错&#xff0c; 全局安装 ts 也不行 通过 npx tsc --init 就可以解决

【环境变量】常见的环境变量 | 相关指令 | 环境变量系统程序的结合理解 | 环境变量表 | 本地变量环境变量 | 外部命令内建命令

目录 常见的环境变量 HOME PWD SHELL HISTSIZE 环境变量相关的指令 echo&env export unset 本地变量 环境变量整体理解 程序现象_代码查看环境变量 ​整体理解 环境变量表 环境变量表的传递 环境变量表的查看 内建命令 少说废话&#x1f197; 每个用…

python爬虫 爬取网页图片

http://t.csdnimg.cn/iQgHw //爬虫爬取图片其实是很简单的&#xff0c;但是大多数同学&#xff0c;可能对 url的设置一直有困惑&#xff08;这点本人也在研究&#xff09;&#xff0c;而本篇文章&#xff0c;对于想要爬取图片的小白简直是福利。你只需要将文章代码运行即可&am…

超图SuperMap-Cesium,地形图层,可以渲染一个或多个地形(地形可缓存DEM,TIN方式),webGL代码开发(2024-04-08)

1、缓存文件类型TIN格式&#xff0c;TIN的地形sct只能加一个 const viewer new Cesium.Viewer(cesiumContainer); viewer.terrainProvider new Cesium.CesiumTerrainProvider({isSct: true, // 是否为iServer发布的TIN地形服务,stk地形设置为falserequestWaterMask : true,…

AI日报:GPT-4-Turbo正式版自带读图能力;Gemini1.5Pro开放API;SD3将于4月中旬发布;抖音宫崎骏AI特效爆火

欢迎来到【AI日报】栏目!这里是你每天探索人工智能世界的指南&#xff0c;每天我们为你呈现AI领域的热点内容&#xff0c;聚焦开发者&#xff0c;助你洞悉技术趋势、了解创新AI产品应用。 新鲜AI产品点击了解&#xff1a;AIbase - 智能匹配最适合您的AI产品和网站 &#x1f4f…

springboot 创建子模块时 pom 配置

创建子模块 2. 修改父模块 pom 文件 添加如下内容 <packaging>pom</packaging><modules><module>mybatisconf</module></modules>3. 修改子模块 pom 文件 <parent><groupId>com.vazquez</groupId><artifactId>bo…

TensorFlow学习之:深度学习基础

神经网络基础 神经网络是深度学习的核心&#xff0c;它们受人脑的结构和功能启发&#xff0c;能够通过学习大量数据来识别模式和解决复杂问题。神经网络的基本工作原理包括前向传播和反向传播两个阶段。 前向传播&#xff08;Forward Propagation&#xff09; 前向传播是神经…

全国水科技大会 免费征集《水环境治理减污降碳协同增效示范案例》

申报时间截止到2024年4月15日&#xff0c;请各单位抓紧申报&#xff0c;申报条件及申报表请联系&#xff1a;13718793867 围绕水环境治理减污降碳协同增效领域&#xff0c;以资源化、生态化和可持续化为导向&#xff0c;面向生态、流城、城市、农村、工业园区、电力、石化、钢…

在VsCode中写vue的css,代码提示一直不出现或提示错误

在我们vue项目正常写css样式&#xff0c;便会出现一下提示&#xff0c;如&#xff1a; 但有时无提示&#xff0c;那么这种情况有以下几种解决方案 观察Vscode插件是否正常 Vetur和Vue - Official是否安装(Vue - Official的前身就是Volar) 安装了检查是否最新版本 确保你的s…

el-table 设置固定列导致行错位的解决方案

element 官方给出的解决办法是使用doLayout&#xff0c;使用doLayout重新加载一下table就好了。 updated() {// tableRef是表格的ref属性值if (this.$refs.tableRef&& this.$refs.tableRef.doLayout) {this.$refs.tableRef.doLayout();}},调整前后效果对比&#xff1a…

lua学习笔记14(协程的学习)

print("*****************************协程的学习*******************************") --创建1 coroutine.create(function()) 使用1 coroutine.resume(co) -- 创建2 co2coroutine.wrap(fun) 使用2 co2() --协程的挂起函数 coroutine.yield() --协程的状态 --c…

跨学科高手揭秘:仿真技术如何改变工程世界

编辑 / 木子 审核 / 朝阳 “在高铁上睡觉&#xff0c;最大的噪音不是来自车轮与铁轨的摩擦声&#xff0c;也不是汽笛的轰鸣&#xff0c;而是巨大的‘嘶嘶’声——那是我大学时期做实验发出的声音。”12月9日&#xff0c;中国科学技术大学2024届毕业生郭骞在“伟骅科技”公众号…

4.19号驱动

1. ARM裸机开发和Linux系统开发的异同 相同点&#xff1a;都是对硬件进行操作 不同点&#xff1a; 有无操作系统 是否具备多进程多线程开发 是否可以调用库函数 操作地址是否相同&#xff0c;arm操作物理地址&#xff0c;驱动操作虚拟地址 2. Linux操作系统的层次 应用层…

(2022级)成都工业学院数据库原理及应用实验二:CASE工具关系模型建模

写在前面 1、基于2022级软件工程/计算机科学与技术实验指导书 2、代码仅提供参考 3、如果代码不满足你的要求&#xff0c;请寻求其他的途径 运行环境 window11家庭版 PowerDesigner 16.1 实验要求 某医院一个门诊部排班管理子系统涉及如下信息&#xff1a; 若干科室&a…

成都百洲文化传媒有限公司靠谱吗?怎么样?

随着互联网的迅猛发展&#xff0c;电子商务行业迎来了前所未有的发展机遇。在这个变革的浪潮中&#xff0c;成都百洲文化传媒有限公司凭借其深厚的行业经验和创新的服务模式&#xff0c;正逐渐成为电商服务领域的新领军者。 一、创新引领&#xff0c;塑造电商服务新标准 成都百…

Windows下docker-compose部署DolphinScheduler

参照&#xff1a;快速上手 - Docker部署(Docker) - 《Apache DolphinScheduler v3.1.0 使用手册》 - 书栈网 BookStack 下载源文件 地址&#xff1a;https://dolphinscheduler.apache.org/zh-cn/download/3.2.1 解压到指定目录&#xff0c;进入apache-dolphinscheduler-xxx-…

vscode开发小程序项目并在微信开发者工具运行

需求&#xff1a;vscode开发uniapp之后在微信开发者工具运行&#xff0c;更改的时候微信开发者也同步更改 创建微信小程序所需插件&#xff0c;在vscode的插件管理里面安装就可以了 1.微信小程序开发工具 2.vscode weapp api 3.vscode wxml 4.vscode wechat 1.创建小程序命…