JAVA 设计模式:观察者、状态、策略、模板方法、访问者
本文将深入探讨五种常见的 Java 设计模式:观察者模式、状态模式、策略模式、模板方法模式和访问者模式,并提供丰富的实例帮助理解和应用。
1. 观察者模式 (Observer Pattern)
定义: 定义对象间的一种一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。
结构:
- 主题 (Subject):被观察的对象,维护观察者列表,并提供注册、取消注册和通知观察者的方法。
- 观察者 (Observer):依赖主题的对象,定义更新方法以响应主题状态变化。
示例:
假设我们有一个 WeatherStation
类,它负责收集天气数据并更新 Temperature
属性。我们希望多个用户 (如 Display
类) 可以实时查看温度变化。
// 主题
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 观察者
public interface Observer {
void update(float temperature);
}
// 主题实现
public class WeatherStation implements Subject {
private List<Observer> observers = new ArrayList<>();
private float temperature;
public void registerObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
public void notifyObservers() {
observers.forEach(observer -> observer.update(temperature));
}
public void setTemperature(float temperature) {
this.temperature = temperature;
notifyObservers();
}
}
// 观察者实现
public class Display implements Observer {
@Override
public void update(float temperature) {
System.out.println("温度更新:" + temperature + " 度");
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
WeatherStation station = new WeatherStation();
Display display = new Display();
station.registerObserver(display);
station.setTemperature(25.5f); // 通知观察者更新
}
}
优点:
- 松耦合:主题和观察者之间没有直接依赖关系,降低了代码复杂度。
- 灵活性:可根据需要添加或移除观察者,易于扩展。
应用场景:
- 事件处理:如 GUI 事件处理、数据库监听等。
- 通知系统:如邮件通知、短信通知等。
2. 状态模式 (State Pattern)
定义: 将一个对象的内部状态封装成不同的状态类,并允许对象根据其状态改变其行为。
结构:
- 状态 (State):抽象状态类,定义状态的行为方法。
- 具体状态 (ConcreteState):实现抽象状态类,定义不同状态的行为。
- 上下文 (Context):维护当前状态,并提供切换状态的方法。
示例:
假设我们有一个 TrafficLight
类,它可以处于红灯、黄灯或绿灯三种状态。
// 状态接口
public interface State {
void handleRequest();
}
// 具体状态
public class RedState implements State {
@Override
public void handleRequest() {
System.out.println("红灯状态:停止");
}
}
public class YellowState implements State {
@Override
public void handleRequest() {
System.out.println("黄灯状态:准备");
}
}
public class GreenState implements State {
@Override
public void handleRequest() {
System.out.println("绿灯状态:通行");
}
}
// 上下文
public class TrafficLight {
private State state;
public TrafficLight() {
state = new RedState();
}
public void setState(State state) {
this.state = state;
}
public void handleRequest() {
state.handleRequest();
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
TrafficLight light = new TrafficLight();
light.handleRequest(); // 输出:红灯状态:停止
light.setState(new YellowState());
light.handleRequest(); // 输出:黄灯状态:准备
}
}
优点:
- 代码可读性更高:将状态逻辑分离到不同的类中,易于理解和维护。
- 扩展性强:可轻松添加新的状态,而不会影响其他状态逻辑。
应用场景:
- 状态机:如游戏角色状态、流程状态等。
- 对象生命周期管理:如用户状态管理、订单状态管理等。
3. 策略模式 (Strategy Pattern)
定义: 定义一系列算法,将每个算法封装成独立的类,并让它们可以相互替换。
结构:
- 策略 (Strategy):抽象策略类,定义算法接口。
- 具体策略 (ConcreteStrategy):实现抽象策略类,定义具体的算法。
- 上下文 (Context):维护当前策略,并提供执行策略的方法。
示例:
假设我们有一个 ShoppingCart
类,它需要根据不同的折扣策略计算总价。
// 策略接口
public interface DiscountStrategy {
double calculateDiscount(double price);
}
// 具体策略
public class NoDiscount implements DiscountStrategy {
@Override
public double calculateDiscount(double price) {
return price;
}
}
public class TenPercentDiscount implements DiscountStrategy {
@Override
public double calculateDiscount(double price) {
return price * 0.9;
}
}
// 上下文
public class ShoppingCart {
private DiscountStrategy strategy;
public ShoppingCart(DiscountStrategy strategy) {
this.strategy = strategy;
}
public double getTotalPrice(double price) {
return strategy.calculateDiscount(price);
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart(new NoDiscount());
double total1 = cart.getTotalPrice(100); // 总价:100
cart = new ShoppingCart(new TenPercentDiscount());
double total2 = cart.getTotalPrice(100); // 总价:90
}
}
优点:
- 可扩展性:可轻松添加新的策略,而不会影响现有代码。
- 代码可读性更高:将算法逻辑分离到不同的类中,易于理解和维护。
应用场景:
- 算法选择:如排序算法、加密算法、压缩算法等。
- 不同的业务规则:如不同的折扣策略、不同的配送方式等。
4. 模板方法模式 (Template Method Pattern)
定义: 定义一个算法的骨架,将一些步骤延迟到子类中实现。模板方法模式允许子类重定义算法中某些特定步骤,但不改变算法的整体结构。
结构:
- 抽象类 (AbstractClass):定义模板方法,其中包含算法骨架,部分步骤使用抽象方法。
- 具体子类 (ConcreteClass):实现抽象方法,完成算法的具体步骤。
示例:
假设我们有一个 Game
类,它定义了游戏的基本流程,但具体的步骤 (如游戏开始、玩家操作、游戏结束) 由子类实现。
// 抽象类
public abstract class Game {
public void play() {
initialize();
start();
while (!isOver()) {
playTurn();
}
end();
}
protected abstract void initialize();
protected abstract void start();
protected abstract void playTurn();
protected abstract boolean isOver();
protected abstract void end();
}
// 具体子类
public class ChessGame extends Game {
@Override
protected void initialize() {
System.out.println("准备棋盘...");
}
@Override
protected void start() {
System.out.println("游戏开始!");
}
@Override
protected void playTurn() {
System.out.println("玩家回合...");
}
@Override
protected boolean isOver() {
return false; // 游戏未结束
}
@Override
protected void end() {
System.out.println("游戏结束!");
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
ChessGame chess = new ChessGame();
chess.play(); // 执行游戏流程
}
}
优点:
- 代码复用性高:子类可以复用模板方法中的公共代码。
- 可扩展性强:子类可以重写抽象方法,实现不同的游戏逻辑。
应用场景:
- 定义算法骨架,允许子类自定义部分步骤。
- 框架设计:如数据库操作框架、网络通信框架等。
5. 访问者模式 (Visitor Pattern)
定义: 表示一个作用于某对象结构中的各元素的操作。它允许你定义新的操作,而不改变这些元素的类。
结构:
- 访问者 (Visitor):定义访问不同元素的操作方法。
- 具体访问者 (ConcreteVisitor):实现访问者接口,定义具体的操作。
- 元素 (Element):接受访问者,并提供接受访问者方法。
- 具体元素 (ConcreteElement):实现元素接口,接受访问者并执行访问者的操作。
示例:
假设我们有一个 Employee
类,它可以是 Engineer
或 Manager
。我们希望根据员工类型执行不同的操作,如打印信息或计算工资。
// 访问者接口
public interface Visitor {
void visit(Engineer engineer);
void visit(Manager manager);
}
// 具体访问者
public class PrintVisitor implements Visitor {
@Override
public void visit(Engineer engineer) {
System.out.println("工程师:" + engineer.getName());
}
@Override
public void visit(Manager manager) {
System.out.println("经理:" + manager.getName());
}
}
public class SalaryVisitor implements Visitor {
@Override
public void visit(Engineer engineer) {
System.out.println("工程师工资:" + engineer.getSalary());
}
@Override
public void visit(Manager manager) {
System.out.println("经理工资:" + manager.getSalary());
}
}
// 元素接口
public interface Element {
void accept(Visitor visitor);
}
// 具体元素
public class Engineer implements Element {
private String name;
private double salary;
public Engineer(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class Manager implements Element {
private String name;
private double salary;
public Manager(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
Engineer engineer = new Engineer("张三", 10000);
Manager manager = new Manager("李四", 20000);
PrintVisitor printer = new PrintVisitor();
SalaryVisitor calculator = new SalaryVisitor();
engineer.accept(printer);
manager.accept(printer);
engineer.accept(calculator);
manager.accept(calculator);
}
}
优点:
- 可扩展性:可轻松添加新的操作,而不会影响元素类。
- 代码可读性更高:将操作逻辑分离到不同的访问者类中。
应用场景:
- 处理各种操作:如打印、序列化、数据验证等。
- 对象结构复杂:如树结构、图结构等。
总结:
这五种设计模式是 Java 开发中常用的设计模式,它们可以帮助我们编写更优雅、更易维护的代码。通过理解和应用这些模式,我们可以提高代码的复用性、可扩展性和可读性。
提示:
- 选择合适的模式需要根据具体情况进行判断。
- 不要为了使用模式而使用模式,应根据实际需求选择最佳方案。
- 了解模式背后的设计思想,才能更好地应用模式。