Java行为型设计模式深入探讨
一、概述
在软件开发过程中,设计模式是经过反复验证的、用于解决特定问题的通用解决方案。行为型设计模式关注的是对象之间的交互和职责分配,通过合理的设计,使得系统中的对象能够协同工作,实现复杂的功能。Java作为一种广泛应用的编程语言,为实现各种行为型设计模式提供了良好的基础。下面将深入探讨Java中常见的行为型设计模式。
二、策略模式(Strategy Pattern)
2.1 概念
策略模式定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。策略模式使得算法的变化独立于使用算法的客户。
2.2 结构
策略模式包含以下主要角色:
- 抽象策略(Strategy):定义了一个公共接口,各种具体策略类实现这个接口。
- 具体策略(ConcreteStrategy):实现抽象策略定义的接口,提供具体的算法实现。
- 环境(Context):持有一个抽象策略类的引用,提供给客户端调用的接口,并根据需要在运行时切换具体策略。
2.3 代码示例
// 抽象策略
interface Strategy {
int execute(int num1, int num2);
}
// 具体策略1:加法
class AddStrategy implements Strategy {
@Override
public int execute(int num1, int num2) {
return num1 + num2;
}
}
// 具体策略2:减法
class SubtractStrategy implements Strategy {
@Override
public int execute(int num1, int num2) {
return num1 - num2;
}
}
// 环境
class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2) {
return strategy.execute(num1, num2);
}
}
测试代码:
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new AddStrategy());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new Context(new SubtractStrategy());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
}
}
2.4 应用场景
- 当一个系统需要动态地在几种算法中选择一种时,可使用策略模式。
- 避免使用多重条件判断语句,将不同的行为封装到不同的策略类中。
三、观察者模式(Observer Pattern)
3.1 概念
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,会通知所有观察者对象,使它们能够自动更新。
3.2 结构
- 主题(Subject):被观察的对象,持有一系列观察者的引用,并提供添加、删除观察者以及通知观察者的方法。
- 观察者(Observer):定义了一个更新接口,当主题状态改变时,主题会调用这个接口通知观察者。
- 具体主题(ConcreteSubject):实现主题接口,负责维护主题的状态,并在状态改变时通知观察者。
- 具体观察者(ConcreteObserver):实现观察者接口,具体实现更新操作。
3.3 代码示例
import java.util.ArrayList;
import java.util.List;
// 主题
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 具体主题
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) {
int i = observers.indexOf(observer);
if (i >= 0) {
observers.remove(i);
}
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(temperature, humidity, pressure);
}
}
public void measurementsChanged() {
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
// 观察者
interface Observer {
void update(float temperature, float humidity, float pressure);
}
// 具体观察者1:当前天气显示
class CurrentConditionsDisplay implements Observer {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
public void display() {
System.out.println("Current conditions: " + temperature + "°C, " + humidity + "% humidity");
}
}
测试代码:
public class ObserverPatternDemo {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(25, 60, 1013);
}
}
3.4 应用场景
- 当一个对象的状态改变需要通知其他对象,并且不知道具体有多少对象需要被通知时,可使用观察者模式。
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,可使用观察者模式将这两者封装在独立的对象中,使它们可以各自独立地改变和复用。
四、模板方法模式(Template Method Pattern)
4.1 概念
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
4.2 结构
- 抽象类(AbstractClass):定义了模板方法,该方法包含了算法的骨架,调用一些抽象的或具体的方法。
- 具体类(ConcreteClass):继承抽象类,实现抽象类中定义的抽象方法,提供具体的实现。
4.3 代码示例
// 抽象类
abstract class AbstractClass {
// 模板方法
public final void templateMethod() {
primitiveOperation1();
primitiveOperation2();
concreteOperation();
hook();
}
protected abstract void primitiveOperation1();
protected abstract void primitiveOperation2();
// 具体方法
protected void concreteOperation() {
System.out.println("This is a concrete operation.");
}
// 钩子方法,默认实现为空,子类可根据需要重写
protected void hook() {}
}
// 具体类1
class ConcreteClass1 extends AbstractClass {
@Override
protected void primitiveOperation1() {
System.out.println("ConcreteClass1's implementation of primitiveOperation1");
}
@Override
protected void primitiveOperation2() {
System.out.println("ConcreteClass1's implementation of primitiveOperation2");
}
@Override
protected void hook() {
System.out.println("ConcreteClass1's implementation of hook method");
}
}
// 具体类2
class ConcreteClass2 extends AbstractClass {
@Override
protected void primitiveOperation1() {
System.out.println("ConcreteClass2's implementation of primitiveOperation1");
}
@Override
protected void primitiveOperation2() {
System.out.println("ConcreteClass2's implementation of primitiveOperation2");
}
}
测试代码:
public class TemplateMethodPatternDemo {
public static void main(String[] args) {
AbstractClass concreteClass1 = new ConcreteClass1();
concreteClass1.templateMethod();
AbstractClass concreteClass2 = new ConcreteClass2();
concreteClass2.templateMethod();
}
}
4.4 应用场景
- 当多个子类有公共的行为,并且这些公共行为可以被提取到父类中,使用模板方法模式可以避免代码重复。
- 当需要通过子类来扩展算法的某些步骤时,可使用模板方法模式。
五、迭代器模式(Iterator Pattern)
5.1 概念
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
5.2 结构
- 迭代器(Iterator):定义了访问和遍历元素的接口,如
next()
、hasNext()
等方法。 - 具体迭代器(ConcreteIterator):实现迭代器接口,跟踪遍历的当前位置。
- 聚合(Aggregate):定义创建迭代器对象的接口。
- 具体聚合(ConcreteAggregate):实现聚合接口,返回一个具体迭代器的实例。
5.3 代码示例
import java.util.ArrayList;
import java.util.List;
// 迭代器
interface Iterator {
boolean hasNext();
Object next();
}
// 具体迭代器
class ConcreteIterator implements Iterator {
private List<Object> list;
private int position = 0;
public ConcreteIterator(List<Object> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return position < list.size();
}
@Override
public Object next() {
if (this.hasNext()) {
return list.get(position++);
}
return null;
}
}
// 聚合
interface Aggregate {
Iterator createIterator();
}
// 具体聚合
class ConcreteAggregate implements Aggregate {
private List<Object> list = new ArrayList<>();
public void addObject(Object obj) {
list.add(obj);
}
public void removeObject(Object obj) {
list.remove(obj);
}
@Override
public Iterator createIterator() {
return new ConcreteIterator(list);
}
}
测试代码:
public class IteratorPatternDemo {
public static void main(String[] args) {
ConcreteAggregate aggregate = new ConcreteAggregate();
aggregate.addObject("Item 1");
aggregate.addObject("Item 2");
aggregate.addObject("Item 3");
Iterator iterator = aggregate.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
5.4 应用场景
- 当需要访问一个聚合对象的内容而又不暴露其内部细节时,可使用迭代器模式。
- 当需要为聚合对象提供多种遍历方式时,迭代器模式可以很方便地实现。
六、责任链模式(Chain of Responsibility Pattern)
6.1 概念
责任链模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
6.2 结构
- 抽象处理者(Handler):定义了处理请求的接口,同时包含一个后继者的引用。
- 具体处理者(ConcreteHandler):实现抽象处理者的处理方法,判断自己是否能处理该请求,如果能则处理,否则将请求传递给后继者。
- 客户(Client):向链上的具体处理者对象提交请求。
6.3 代码示例
// 抽象处理者
abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public abstract void handleRequest(int request);
}
// 具体处理者1
class ConcreteHandler1 extends Handler {
@Override
public void handleRequest(int request) {
if (request >= 0 && request < 10) {
System.out.println("ConcreteHandler1 handled request " + request);
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
// 具体处理者2
class ConcreteHandler2 extends Handler {
@Override
public void handleRequest(int request) {
if (request >= 10 && request < 20) {
System.out.println("ConcreteHandler2 handled request " + request);
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
// 具体处理者3
class ConcreteHandler3 extends Handler {
@Override
public void handleRequest(int request) {
if (request >= 20 && request < 30) {
System.out.println("ConcreteHandler3 handled request " + request);
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
测试代码:
public class ChainOfResponsibilityPatternDemo {
public static void main(String[] args) {
ConcreteHandler1 handler1 = new ConcreteHandler1();
ConcreteHandler2 handler2 = new ConcreteHandler2();
ConcreteHandler3 handler3 = new ConcreteHandler3();
handler1.setSuccessor(handler2);
handler2.setSuccessor(handler3);
int[] requests = {5, 14, 22, 18, 3, 27, 20};
for (int request : requests) {
handler1.handleRequest(request);
}
}
}
6.4 应用场景
- 当有多个对象可以处理一个请求,哪个对象处理该请求在运行时动态确定时,可使用责任链模式。
- 当需要动态地指定一组对象处理请求,并且希望对请求的处理进行解耦时,责任链模式是一个不错的选择。
七、命令模式(Command Pattern)
7.1 概念
命令模式将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
7.2 结构
- 命令(Command):定义了一个执行操作的接口,通常包含
execute()
方法。 - 具体命令(ConcreteCommand):实现命令接口,绑定一个接收者对象,并调用接收者的相应方法来执行命令。
- 接收者(Receiver):知道如何执行与请求相关的操作,具体命令对象会调用接收者的方法。
- 调用者(Invoker):持有一个命令对象的引用,通过调用命令对象的
execute()
方法来触发请求。 - 客户(Client):创建具体命令对象,并设置其接收者。
7.3 代码示例
// 接收者
class Receiver {
public void action() {
System.out.println("Receiver is performing an action.");
}
}
// 命令
interface Command {
void execute();
}
// 具体命令
class ConcreteCommand implements Command {
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.action();
}
}
// 调用者
class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void executeCommand() {
if (command != null) {
command.execute();
}
}
}
测试代码:
public class CommandPatternDemo {
public static void main(String[] args) {
Receiver receiver = new Receiver();
Command command = new ConcreteCommand(receiver);
Invoker invoker = new Invoker();
invoker.setCommand(command);
invoker.executeCommand();
}
}
7.4 应用场景
- 当需要将请求的发送者和接收者解耦,使得发送者不需要知道接收者的具体信息时,可使用命令模式。
- 当需要支持命令的撤销、恢复操作时,命令模式可以方便地实现。
八、备忘录模式(Memento Pattern)
8.1 概念
备忘录模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
8.2 结构
- 发起人(Originator):创建一个备忘录,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。
- 备忘录(Memento):负责存储发起人对象的内部状态,并可以防止发起人以外的其他对象访问备忘录。
- 管理者(Caretaker):负责保存备忘录,但不能对备忘录的内容进行操作或检查。
8.3 代码示例
// 备忘录
class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// 发起人
class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento saveStateToMemento() {
return new Memento(state);
}
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
// 管理者
class Caretaker {
private Memento memento;
public void saveMemento(Memento memento) {
this.memento = memento;
}
public Memento getMemento() {
return memento;
}
}
测试代码:
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("State1");
originator.setState("State2");
caretaker.saveMemento(originator.saveStateToMemento());
originator.setState("State3");
System.out.println("Current state: " + originator.getState());
originator.getStateFromMemento(caretaker.getMemento());
System.out.println("Restored state: " + originator.getState());
}
}
8.4 应用场景
- 当需要保存一个对象在某一时刻的状态,以便以后恢复到该状态时,可使用备忘录模式。
- 当不希望外部对象直接访问对象的内部状态,而又需要保存和恢复该状态时,备忘录模式可以满足需求。
九、状态模式(State Pattern)
9.1 概念
状态模式允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
9.2 结构
- 抽象状态(State):定义了一个接口,封装了与Context的一个特定状态相关的行为。
- 具体状态(ConcreteState):实现抽象状态接口,每个具体状态类都对应Context的一个具体状态,并实现与该状态相关的行为。
- 环境(Context):持有一个抽象状态类的引用,定义了客户端感兴趣的接口,并根据当前状态委托状态对象处理请求。
9.3 代码示例
// 抽象状态
interface State {
void handle(Context context);
}
// 具体状态1
class ConcreteState1 implements State {
@Override
public void handle(Context context) {
System.out.println("Handling request in ConcreteState1");
context.setState(new ConcreteState2());
}
}
// 具体状态2
class ConcreteState2 implements State {
@Override
public void handle(Context context) {
System.out.println("Handling request in ConcreteState2");
context.setState(new ConcreteState1());
}
}
// 环境
class Context {
private State state;
public Context() {
state = new ConcreteState1();
}
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle(this);
}
}
测试代码:
public class StatePatternDemo {
public static void main(String[] args) {
Context context = new Context();
context.request();
context.request();
context.request();
}
}
9.4 应用场景
- 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变行为时,可使用状态模式。
- 当代码中包含大量与对象状态有关的条件语句时,将状态的判断逻辑转移到状态对象中,可以使代码更清晰、可维护性更高。
十、中介者模式(Mediator Pattern)
10.1 概念
中介者模式用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
10.2 结构
- 中介者(Mediator):定义了一个接口用于与各同事对象通信。
- 具体中介者(ConcreteMediator):实现中介者接口,协调各同事对象之间的交互。
- 同事(Colleague):定义了一个抽象类,所有具体同事类都继承自这个类,并持有中介者对象的引用,通过中介者对象与其他同事对象进行通信。
- 具体同事(ConcreteColleague):继承同事抽象类,实现具体的业务逻辑,并在需要与其他同事通信时,通过中介者对象进行。
10.3 代码示例
// 中介者
interface Mediator {
void send(String message, Colleague colleague);
}
// 具体中介者
class ConcreteMediator implements Mediator {
private ConcreteColleague1 colleague1;
private ConcreteColleague2 colleague2;
public void setColleague1(ConcreteColleague1 colleague1) {
this.colleague1 = colleague1;
}
public void setColleague2(ConcreteColleague2 colleague2) {
this.colleague2 = colleague2;
}
@Override
public void send(String message, Colleague colleague) {
if (colleague == colleague1) {
colleague2.notify(message);
} else {
colleague1.notify(message);
}
}
}
// 同事
abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
public abstract void notify(String message);
public abstract void send(String message);
}
// 具体同事1
class ConcreteColleague1 extends Colleague {
public ConcreteColleague1(Mediator mediator) {
super(mediator);
}
@Override
public void notify(String message) {
System.out.println("Colleague1 received message: " + message);
}
@Override
public void send(String message) {
mediator.send(message, this);
}
}
// 具体同事2
class ConcreteColleague2 extends Colleague {
public ConcreteColleague2(Mediator mediator) {
super(mediator);
}
@Override
public void notify(String message) {
System.out.println("Colleague2 received message: " + message);
}
@Override
public void send(String message) {
mediator.send(message, this);
}
}
测试代码:
public class MediatorPatternDemo {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleague1 colleague1 = new ConcreteColleague1(mediator);
ConcreteColleague2 colleague2 = new ConcreteColleague2(mediator);
mediator.setColleague1(colleague1);
mediator.setColleague2(colleague2);
colleague1.send("Hello, Colleague2!");
colleague2.send("Hi, Colleague1!");
}
}
10.4 应用场景
- 当一组对象之间的交互非常复杂,并且这些交互依赖于彼此的状态时,使用中介者模式可以将这些复杂的交互封装到中介者对象中,简化对象之间的关系。
- 当需要通过一个中间对象来协调多个对象之间的交互,以降低对象之间的耦合度时,中介者模式是一个合适的选择。
十一、解释器模式(Interpreter Pattern)
11.1 概念
解释器模式给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
11.2 结构
- 抽象表达式(AbstractExpression):声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。
- 终结符表达式(TerminalExpression):实现与文法中的终结符相关联的解释操作,一个句子中的每个终结符需要一个具体的终结符表达式。
- 非终结符表达式(NonterminalExpression):为文法中的非终结符实现解释操作,对文法中的每一条规则都需要一个非终结符表达式。
- 环境(Context):包含解释器之外的一些全局信息。
- 客户端(Client):构建一个抽象语法树,表示要解释的句子,并调用解释操作。
11.3 代码示例
import java.util.HashMap;
import java.util.Map;
// 抽象表达式
abstract class Expression {
public abstract int interpret(Context context);
}
// 终结符表达式
class TerminalExpression extends Expression {
private String variable;
public TerminalExpression(String variable) {
this.variable = variable;
}
@Override
public int interpret(Context context) {
return context.lookup(variable);
}
}
// 非终结符表达式:加法
class AddExpression extends Expression {
private Expression left;
private Expression right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
return left.interpret(context) + right.interpret(context);
}
}
// 环境
class Context {
private Map<String, Integer> variables = new HashMap<>();
public void assign(String variable, int value) {
variables.put(variable, value);
}
public int lookup(String variable) {
return variables.get(variable);
}
}
测试代码:
public class InterpreterPatternDemo {
public static void main(String[] args) {
Context context = new Context();
context.assign("x", 5);
context.assign("y", 3);
Expression expression = new AddExpression(
new TerminalExpression("x"),
new TerminalExpression("y")
);
System.out.println("x + y = " + expression.interpret(context));
}
}
11.4 应用场景
- 当有一个语言需要解释执行,并且可以将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。
- 当问题重复出现,并且可以用一种简单的语言来进行表达时,解释器模式可以提供一种灵活的解决方案。不过,由于解释器模式会增加系统的复杂性,所以在实际应用中需要谨慎使用。
通过对以上Java行为型设计模式的深入探讨,希望能够帮助开发者更好地理解和应用这些模式,从而在软件开发中构建出更加灵活、可维护和可扩展的系统。在实际项目中,应根据具体的需求和场景,选择合适的设计模式来优化代码结构和提升软件质量。