策略模式
策略模式是一种行为设计模式,它定义了一系列算法,并将每一个算法封装起来,让它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。
uses > Context +contextInterface() Strategy +algorithm() ConcreteStrategyA +algorithm() ConcreteStrategyB +algorithm()
- Context:上下文角色,用来操作策略的上下文环境,起到承上启下的作用,屏蔽高层模块对策略、算法的直接访问。
- Stragety:抽象策略角色,策略、算法的抽象,通常为接口。
- ConcreteStragety:具体的策略实现。
简单实现
策略模式的简单实现通常涉及以下几个部分:定义一个策略接口,然后根据不同的算法实现该接口,最后在一个上下文类中使用这些策略。以下是一个简化的示例:
(1)策略接口
策略接口定义了一个方法,所有的具体策略类都需要实现这个方法。
java
public interface Strategy {
int execute(int a, int b);
}
(2)具体策略类
我们实现不同的策略类来执行不同的算法。比如,加法
、减法
、乘法
。
java
// 加法策略
public class AddStrategy implements Strategy {
@Override
public int execute(int a, int b) {
return a + b;
}
}
// 减法策略
public class SubtractStrategy implements Strategy {
@Override
public int execute(int a, int b) {
return a - b;
}
}
// 乘法策略
public class MultiplyStrategy implements Strategy {
@Override
public int execute(int a, int b) {
return a * b;
}
}
(3)上下文类
上下文类 Context
使用策略接口来调用具体的策略类。
java
public class Context {
private Strategy strategy;
// 设置策略
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
// 执行策略
public int executeStrategy(int a, int b) {
return strategy.execute(a, b);
}
}
(4)测试类
在测试类中,我们可以通过设置不同的策略来运行不同的算法。
java
public class StrategyPatternExample {
public static void main(String[] args) {
Context context = new Context();
// 使用加法策略
context.setStrategy(new AddStrategy());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
// 使用减法策略
context.setStrategy(new SubtractStrategy());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
// 使用乘法策略
context.setStrategy(new MultiplyStrategy());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}
(5)输出
text
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
使用场景和优缺点
-
使用场景:
- 对客户隐藏具体策略(算法)的实现细节,彼此完全独立
- 针对同一类型问题的多种处理方式,仅具体行为有差别时
- 在一个类中定义了很多行为,而且这些行为在这个类里的操作以多个条件语句的形式出现。策略模式将相关的条件分支移入它们各自的Strategy类中,以代替这些条件语句
-
优点:
- 使用策略模式可以避免使用多重条件语句。多重条件语句不易维护,而且易出错
- 易于拓展。当需要添加一个策略时,只需要实现接口就可以了
-
缺点:
- 每一个策略都是一个类,复用性小。如果策略过多,类的数量会增多
- 上层模块必须知道有哪些策略,才能够使用这些策略,这与迪米特原则相违背
模版方法模式
模版方法模式是一种行为设计模式,它定义了一个操作中的算法骨架,并允许子类在不改变算法结构的情况下,重新定义算法的某些步骤。该模式通过继承实现,父类负责封装大致流程,而具体细节则由子类提供。
AbstractClass -stepOne() -stepTwo() -stepThree() +execute() ConcreteClassA ConcreteClassB
- 抽象类(AbstractClass):定义了算法的模板方法和一些基本方法。模板方法一般是一个封装了算法骨架的固定方法,它不允许子类修改,只有算法的个别步骤可以由子类重写。
- 具体类(ConcreteClass):实现抽象类中的某些方法,具体实现各个步骤的细节。
简单实现
假设我们有一个抽象的制作咖啡和茶的流程。虽然制作的步骤相似(比如加热水、倒水等),但是具体的饮料制作细节有所不同(如茶需要浸泡,而咖啡需要过滤)。
(1)抽象类
java
public abstract class Beverage {
// 模版方法,定义了制作饮料的骨架
public final void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
// 固定步骤:烧水
private void boilWater() {
System.out.println("Boiling water");
}
// 固定步骤:倒入杯中
private void pourInCup() {
System.out.println("Pouring into cup");
}
// 变动步骤:由子类实现
protected abstract void brew(); // 不同饮料的冲泡方式不同
// 变动步骤:由子类实现
protected abstract void addCondiments(); // 不同饮料的调料不同
}
(2)具体类:咖啡
java
public class Coffee extends Beverage {
@Override
protected void brew() {
System.out.println("Brewing coffee");
}
@Override
protected void addCondiments() {
System.out.println("Adding sugar and milk");
}
}
(3)具体类:茶
java
public class Tea extends Beverage {
@Override
protected void brew() {
System.out.println("Steeping the tea");
}
@Override
protected void addCondiments() {
System.out.println("Adding lemon");
}
}
(4)客户端代码
java
public class TemplateMethodExample {
public static void main(String[] args) {
Beverage coffee = new Coffee();
Beverage tea = new Tea();
System.out.println("Making coffee:");
coffee.prepareRecipe(); // 按照模板做咖啡
System.out.println("\nMaking tea:");
tea.prepareRecipe(); // 按照模板做茶
}
}
(5)输出
text
Making coffee:
Boiling water
Brewing coffee
Pouring into cup
Adding sugar and milk
Making tea:
Boiling water
Steeping the tea
Pouring into cup
Adding lemon
使用场景和优缺点
-
使用场景:
-
多个子类有共有的方法,并且逻辑基本相同时
-
面对重要、复杂的算法,可以把核心算法设计为模板方法,周边相关细节功能则由各个子类实现。
-
需要通过子类来决定父类算法中的某个步骤是否执行,实现子类对父类的反向控制。
-
-
优点:
- 模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。子类实现算法的某
- 些细节,有助于算法的扩展。
-
缺点:
- 每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象
观察者模式
观察者模式是一种行为设计模式,用于定义对象之间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会自动得到通知并更新。这个模式通常用于事件驱动的系统中。
+subject 1 * Subject +attach(o: Observer) +detach(o: Observer) +notify() Observer +update() ConcreteSubject ConcreteObserver
- Subject:抽象主题(抽象被观察者)。抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加或删除观察者对象。
- ConcreteSubiect:具体主题(具体被观察者)。该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
- Observer:抽象观察者,是观察者的抽象类。它定义了一个更新接口,使得在得到主题更改通知时更新自己。
- ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态
简单实现
假设有一个天气预报系统,天气变化时,多个设备(如温度显示、气象站等)需要接收到天气变化的通知。
(1)主题(被观察者)接口
java
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
(2)观察者接口
java
public interface Observer {
void update(float temperature, float humidity, float pressure);
}
(3)具体主题(天气数据)
java
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(temperature, humidity, pressure);
}
}
// 更新天气数据
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
notifyObservers(); // 数据更新后通知所有观察者
}
}
(4)具体观察者(温度显示)
java
public class TemperatureDisplay implements Observer {
private float temperature;
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
display();
}
public void display() {
System.out.println("Temperature Display: " + temperature + "°C");
}
}
(5)具体观察者(气象站)
java
public class WeatherStation implements Observer {
private float temperature;
private float humidity;
private float pressure;
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display() {
System.out.println("Weather Station - Temperature: " + temperature + "°C, Humidity: " + humidity + "%, Pressure: " + pressure + "hPa");
}
}
(6)客户端代码(测试)
java
public class ObserverPatternExample {
public static void main(String[] args) {
// 创建主题对象
WeatherData weatherData = new WeatherData();
// 创建观察者对象
TemperatureDisplay tempDisplay = new TemperatureDisplay();
WeatherStation weatherStation = new WeatherStation();
// 注册观察者
weatherData.registerObserver(tempDisplay);
weatherData.registerObserver(weatherStation);
// 更新天气数据并通知所有观察者
weatherData.setMeasurements(25.5f, 65.0f, 1013.1f);
weatherData.setMeasurements(27.0f, 60.0f, 1012.5f);
}
}
(7)输出
text
Temperature Display: 25.5°C
Weather Station - Temperature: 25.5°C, Humidity: 65.0%, Pressure: 1013.1hPa
Temperature Display: 27.0°C
Weather Station - Temperature: 27.0°C, Humidity: 60.0%, Pressure: 1012.5hPa
使用场景和优缺点
- 使用场景:
- 关联行为场景。需要注意的是,关联行为是可拆分的,而不是"组合"关系
- 事件多级触发场景
- 跨系统的消息交换场景,如消息队列、事件总线的处理机制。
- 优点:
- 观察者和被观察者之间是抽象耦合的,容易扩展。
- 方便建立一套触发机制。
- 缺点:在应用观察者模式时需要考虑一下开发效率和运行效率的问题。程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂;而且在Java中消息的通知一般是顺序执行的,那么一个观察者卡顿,会影响整体的执行效率。在这种情况下,一般会采用异步方式。
已经到底啦!!