引言
在软件开发中,随着系统规模和复杂度的增加,如何编写可维护、可扩展的代码成为了开发者面临的重大挑战。此时,设计模式应运而生,它为我们提供了一些经过验证的解决方案,可以帮助我们在复杂问题中找到最优解。设计模式并不是代码的具体实现,而是编程过程中经常遇到的通用问题的解决方案和思路。
设计模式分为三大类:创建型、结构型和行为型。本文将详细讲解这些模式,配以实际的 Java 代码实现和应用场景,帮助读者逐步掌握设计模式的精髓。
第一部分:创建型设计模式
创建型设计模式主要关注如何实例化对象,提供了一些通用的创建对象的方法,从而避免了直接通过 new
关键字来创建对象。这些模式通过不同的方式控制对象的创建过程,达到提高灵活性和可维护性的目的。
1. 单例模式(Singleton Pattern)
定义
单例模式是一种常用的创建型模式,目的是确保某个类在应用中只有一个实例,并提供一个全局访问点。这个实例通常用于管理系统的全局状态,例如配置文件、数据库连接等。
代码示例:懒汉式单例(线程安全)
public class Singleton {
// 记录唯一实例
private static Singleton instance;
// 私有构造函数,防止外部直接创建实例
private Singleton() {}
// 提供全局访问点,懒加载方式
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) { // 双重检查锁定
instance = new Singleton();
}
}
}
return instance;
}
}
解释
getInstance
方法使用双重检查锁定(Double-Checked Locking)来确保线程安全。- 只有在第一次调用
getInstance
方法时才创建实例,避免了不必要的性能开销。
应用场景
- 配置管理器:程序只需要一个配置对象来存储所有全局配置信息。
- 日志管理器:所有日志输出都由一个单例对象统一处理。
- 数据库连接池:为了避免每次都创建连接对象,使用单例模式管理数据库连接池。
优缺点
- 缺点:实现复杂,尤其在多线程环境下的处理相对较麻烦。
2. 工厂模式(Factory Pattern)
定义
工厂模式通过提供一个创建对象的接口,允许子类决定实例化哪个类。工厂方法将对象的创建过程封装,客户代码只需要使用工厂提供的接口来获得对象,无需关心对象的具体创建过程。
代码示例
// 抽象产品类
public abstract class Product {
public abstract void show();
}
// 具体产品类A
public class ProductA extends Product {
public void show() {
System.out.println("This is Product A");
}
}
// 具体产品类B
public class ProductB extends Product {
public void show() {
System.out.println("This is Product B");
}
}
// 工厂类
public class ProductFactory {
public static Product createProduct(String type) {
if ("A".equals(type)) {
return new ProductA();
} else if ("B".equals(type)) {
return new ProductB();
}
return null;
}
}
解释
ProductFactory
类根据传入的 type
参数决定创建哪个产品的实例。客户调用 createProduct
方法时,只需关心产品类型,而不需要了解产品的具体实现。
应用场景
- UI控件库:根据需求创建不同类型的控件对象(按钮、文本框、复选框等)。
- 数据库连接池:通过工厂方法创建不同类型的数据库连接。
- 日志记录器:根据需要创建不同类型的日志对象(文本文件日志、数据库日志等)。
优缺点
- 优点:符合开闭原则(对扩展开放,对修改关闭)。如果需要增加新的产品类型,只需扩展工厂类,无需修改已有代码。
- 缺点:当产品类型增多时,工厂方法可能变得庞大,难以维护。
3. 抽象工厂模式(Abstract Factory Pattern)
定义
抽象工厂模式为创建一组相关的对象提供一个接口,而不需要指定具体的类。它提供了一个更为抽象的工厂,用于创建相关对象族(即一组有共同特征的对象)。
代码示例
// 抽象产品A
public interface ProductA {
void showA();
}
// 抽象产品B
public interface ProductB {
void showB();
}
// 具体产品A1
public class ProductA1 implements ProductA {
public void showA() {
System.out.println("This is Product A1");
}
}
// 具体产品B1
public class ProductB1 implements ProductB {
public void showB() {
System.out.println("This is Product B1");
}
}
// 具体工厂1
public class ConcreteFactory1 {
public ProductA createProductA() {
return new ProductA1();
}
public ProductB createProductB() {
return new ProductB1();
}
}
解释
- 在抽象工厂模式中,我们通过多个工厂来创建一组相关产品。例如,
ConcreteFactory1
类创建了 ProductA1
和 ProductB1
,它们属于同一组产品。
应用场景
- GUI框架:可以创建多个风格的用户界面(例如:Windows风格、Mac风格),每个风格的控件都有相应的工厂类来创建相应的控件。
- 数据库引擎:根据不同的数据库系统(MySQL、Oracle等)创建对应的数据库连接。
优缺点
- 优点:通过抽象工厂,可以确保一组产品的一致性。易于扩展新的产品系列。
- 缺点:增加了系统的复杂度,特别是当产品家族增加时,工厂类也需要做相应的修改。
好的,继续从结构型设计模式部分开始。
第二部分:结构型设计模式
结构型设计模式关注如何将类或对象组合成更大的结构,能够帮助我们在保持灵活性的同时,减少系统的复杂度。结构型模式通过不同方式提供了解耦和扩展性。
1. 适配器模式(Adapter Pattern)
定义
适配器模式使得不兼容的接口可以协同工作。它通过一个中介类来适配不同接口,使得一个类可以在不修改原始代码的情况下与其他类交互。
代码示例
// 目标接口
public interface Target {
void request();
}
// 源接口
public class Adaptee {
public void specificRequest() {
System.out.println("Specific request in Adaptee.");
}
}
// 适配器类
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest(); // 通过适配器调用适配的对象方法
}
}
解释
Target
是我们需要的接口,Adaptee
是已有的、不符合 Target
接口的类。Adapter
类通过组合的方式实现了 Target
接口,并将请求转发给 Adaptee
对象,从而使其能与现有系统兼容。
应用场景
- I/O系统:在读取不同格式的文件时,适配器可以帮助我们适配不同的输入输出格式。
- 老旧系统的兼容:对于一些老旧系统的接口,我们可以通过适配器将其适配到新的系统接口中。
优缺点
- 优点:不修改现有代码,只通过适配器类实现接口的兼容。提高系统的灵活性。
- 缺点:增加了系统的复杂性,适配器类可能会增加系统中的类数。
2. 装饰器模式(Decorator Pattern)
定义
装饰器模式允许在不改变对象结构的情况下动态地给对象添加额外的功能。通过包装现有的类来实现扩展功能,而不是继承扩展。
代码示例
// 抽象组件
public interface Component {
void operation();
}
// 具体组件
public class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent operation.");
}
}
// 装饰器类
public class Decorator implements Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation(); // 委托给被装饰的对象
}
}
// 具体装饰器
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void operation() {
super.operation(); // 调用父类方法
addBehavior(); // 增加附加行为
}
private void addBehavior() {
System.out.println("ConcreteDecorator additional behavior.");
}
}
解释
Component
是核心接口,ConcreteComponent
是具体实现,提供了基础功能。Decorator
类通过组合的方式扩展了 Component
的功能。ConcreteDecorator
进一步在父类功能基础上增加了附加行为。
应用场景
- 图形界面:在界面中可以用装饰器来给控件动态增加功能(例如:添加滚动条、边框、颜色等)。
- 日志处理:可以通过装饰器模式动态地为日志对象添加不同的功能,例如:日志的格式化、存储等。
优缺点
- 优点:可以动态扩展对象的功能,且符合开闭原则,不需要修改已有类。
- 缺点:如果装饰器层次过多,可能导致代码变得复杂,难以理解。
3. 代理模式(Proxy Pattern)
定义
代理模式为另一个对象提供代理,以控制对这个对象的访问。代理对象可以控制客户端和真实对象之间的交互,例如延迟加载、权限控制等。
代码示例
// 抽象主题
public interface Subject {
void request();
}
// 真实主题
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject request.");
}
}
// 代理类
public class Proxy implements Subject {
private RealSubject realSubject;
@Override
public void request() {
if (realSubject == null) {
realSubject = new RealSubject(); // 延迟加载
}
realSubject.request(); // 委托给真实主题
}
}
解释
Subject
是公共接口,RealSubject
实现了具体的业务逻辑。Proxy
类作为中介,控制对 RealSubject
的访问,例如延迟加载、访问控制等。
应用场景
- 虚拟代理:延迟对象的创建,直到需要使用时才创建(例如:图片加载、文件读取等)。
优缺点
- 优点:通过代理可以对对象的访问进行控制,能够添加额外的功能,例如懒加载、权限控制等。
- 缺点:增加了系统的复杂性,如果代理层次过多,可能导致代码难以维护。
4. 外观模式(Facade Pattern)
定义
外观模式通过为子系统中的一组接口提供一个统一的高层接口,使得子系统更加容易使用。外观模式为客户端提供了一个简单的接口,隐藏了复杂的子系统。
代码示例
// 子系统类A
public class SubSystemA {
public void operationA() {
System.out.println("SubSystemA operation.");
}
}
// 子系统类B
public class SubSystemB {
public void operationB() {
System.out.println("SubSystemB operation.");
}
}
// 外观类
public class Facade {
private SubSystemA subSystemA;
private SubSystemB subSystemB;
public Facade() {
subSystemA = new SubSystemA();
subSystemB = new SubSystemB();
}
public void performOperations() {
subSystemA.operationA();
subSystemB.operationB();
}
}
解释
Facade
类提供了一个统一的接口,客户端通过它与多个子系统进行交互,避免了直接调用子系统的复杂接口。
应用场景
- 复杂系统的简化接口:例如,一个复杂的银行系统可以通过外观模式提供一个简单的接口供客户进行存款、取款等操作。
- 视频转换工具:可以通过外观模式为用户提供一个简洁的操作接口,隐藏视频处理过程中的复杂操作。
优缺点
- 优点:简化了系统接口,降低了系统复杂度,使得客户端容易使用。
- 缺点:增加了一个额外的层次,可能导致功能被过度封装,降低了灵活性。
5. 桥接模式(Bridge Pattern)
定义
桥接模式将抽象部分与实现部分分离,使得它们可以独立地变化。它通过组合的方式将抽象与实现解耦,从而增加系统的灵活性。
代码示例
// 抽象类
public abstract class Abstraction {
protected Implementor implementor;
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public abstract void operation();
}
// 具体抽象类
public class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
System.out.println("RefinedAbstraction operation.");
implementor.operationImpl();
}
}
// 实现类接口
public interface Implementor {
void operationImpl();
}
// 具体实现类
public class ConcreteImplementorA implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorA implementation.");
}
}
解释
Abstraction
是抽象部分,Implementor
是实现部分。RefinedAbstraction
类进一步扩展了抽象部分的功能。- 通过桥接模式,
Abstraction
和 Implementor
可以独立变化。
应用场景
- 图形绘制系统:不同的图形(如圆形、矩形)可以在不同的操作系统平台上绘制,桥接模式能够让具体图形的绘制和操作系统平台解耦。
- 数据库访问层:不同数据库(如 MySQL、Oracle)可以通过桥接模式来独立扩展和实现数据库访问操作。
优缺点
缺点
- 优点:将抽象部分与实现部分解耦,使得二者可以独立变化,符合开闭原则。
- 缺点:增加了系统的复杂度,特别是在两者之间的关系变得更加复杂时。
第三部分:行为型设计模式
行为型设计模式关注对象之间的通信,描述了如何让不同的对象之间通过适当的方式进行协作和沟通。它通过在对象间传递消息和控制执行的方式来提高系统的灵活性和可扩展性。
1. 策略模式(Strategy Pattern)
定义
策略模式定义一系列算法,并将每一个算法封装起来,使得它们可以互相替换。策略模式使得算法的变化独立于使用算法的客户端。
代码示例
// 策略接口
public interface Strategy {
void execute();
}
// 具体策略A
public class ConcreteStrategyA implements Strategy {
@Override
public void execute() {
System.out.println("Executing Strategy A");
}
}
// 具体策略B
public class ConcreteStrategyB implements Strategy {
@Override
public void execute() {
System.out.println("Executing Strategy B");
}
}
// 上下文类
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void executeStrategy() {
strategy.execute();
}
}
解释
Strategy
是策略的接口,定义了算法的通用方法,ConcreteStrategyA
和 ConcreteStrategyB
是具体的策略实现。Context
类使用策略对象来执行操作,客户端可以动态切换不同的策略。
应用场景
- 排序算法选择:根据不同的排序需求(如快速排序、归并排序等)选择不同的排序策略。
- 文件压缩算法:根据需求选择不同的压缩算法(如 ZIP、RAR 等)。
优缺点
- 优点:策略模式通过将算法封装成独立的类,使得算法的变化独立于客户端,增加了系统的灵活性。
- 缺点:客户端需要知道所有的策略类,并选择合适的策略,可能导致策略类的数量增加,管理上稍显复杂。
2. 观察者模式(Observer Pattern)
定义
观察者模式定义了对象间一对多的依赖关系。当一个对象的状态发生改变时,它的所有依赖者都会得到通知并自动更新。观察者模式适用于事件驱动系统。
代码示例
// 观察者接口
public interface Observer {
void update(String message);
}
// 具体观察者
public class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received message: " + message);
}
}
// 被观察者
public class Subject {
private List<Observer> observers = new ArrayList<>();
public void addObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}
解释
Observer
是观察者接口,定义了接收通知的方法,ConcreteObserver
实现了这一接口,能够在接收到通知时更新自身。Subject
是被观察者,维护着观察者列表,并在状态改变时通知所有观察者。
应用场景
- 事件监听:GUI程序中,按钮点击、窗口关闭等事件可以通过观察者模式来处理。
- 消息推送系统:实时通知用户订阅的内容更新,例如新闻推送、社交媒体更新等。
优缺点
- 优点:观察者模式解耦了被观察者和观察者之间的关系,方便增加或删除观察者。
- 缺点:如果观察者过多,被观察者的状态变化可能会造成较大的性能开销。
3. 状态模式(State Pattern)
定义
状态模式允许对象在内部状态发生变化时改变其行为。状态模式将状态的变化封装到不同的状态对象中,使得每个状态的行为独立,客户端可以通过切换状态来改变对象的行为。
代码示例
// 状态接口
public interface State {
void handle();
}
// 具体状态A
public class ConcreteStateA implements State {
@Override
public void handle() {
System.out.println("Handling in State A");
}
}
// 具体状态B
public class ConcreteStateB implements State {
@Override
public void handle() {
System.out.println("Handling in State B");
}
}
// 上下文类
public class Context {
private State state;
public Context(State state) {
this.state = state;
}
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle();
}
}
解释
State
是状态接口,定义了所有具体状态的处理方法。ConcreteStateA
和 ConcreteStateB
是不同的具体状态,它们实现了 handle
方法以定义在各自状态下的行为。Context
类代表一个状态机,它持有当前的状态,并根据状态的变化来处理不同的请求。
应用场景
- 游戏角色状态:游戏角色的不同状态(如待机、攻击、防御等)可以通过状态模式来管理。
- 工作流管理:不同阶段的任务处理可以通过状态模式来控制任务的流转。
优缺点
- 优点:通过将状态的行为封装到不同的状态对象中,避免了大量的
if-else
语句,增加了代码的可维护性。 - 缺点:如果状态数目过多,可能导致系统的类数急剧增加,增加了系统的复杂度。
4. 责任链模式(Chain of Responsibility Pattern)
定义
责任链模式将多个处理对象连接在一起,形成一条链条。当请求发生时,它沿着链条传递,直到有一个处理对象能够处理该请求为止。责任链模式可以避免请求发送者和接受者之间的紧耦合。
代码示例
// 处理者接口
public interface Handler {
void handleRequest(String request);
void setNextHandler(Handler handler);
}
// 具体处理者A
public class ConcreteHandlerA implements Handler {
private Handler nextHandler;
@Override
public void handleRequest(String request) {
if (request.equals("A")) {
System.out.println("Handled by Handler A");
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
@Override
public void setNextHandler(Handler handler) {
this.nextHandler = handler;
}
}
// 具体处理者B
public class ConcreteHandlerB implements Handler {
private Handler nextHandler;
@Override
public void handleRequest(String request) {
if (request.equals("B")) {
System.out.println("Handled by Handler B");
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
@Override
public void setNextHandler(Handler handler) {
this.nextHandler = handler;
}
}
解释
Handler
定义了处理请求的方法和设置下一个处理者的方法。ConcreteHandlerA
和 ConcreteHandlerB
分别处理不同的请求。如果当前处理者无法处理请求,它会将请求传递给链条中的下一个处理者。
应用场景
- 请求处理管道:例如:HTTP 请求处理流程、审批流程等。
- 日志处理:日志记录器可以使用责任链模式来处理不同级别的日志(如调试、警告、错误)。
优缺点
- 优点:通过责任链模式,能动态地调整请求的处理顺序,符合开闭原则,扩展性较强。
- 缺点:如果链条过长,处理过程可能变得复杂,且不容易调试。
5. 命令模式(Command Pattern)
定义
命令模式将请求封装为一个对象,从而使用户可以将请求的发送者和接收者解耦。命令模式允许请求的调用者与执行者之间没有直接联系,并且可以通过命令对象的不同组合来执行不同的操作。
代码示例
// 命令接口
public interface Command {
void execute();
}
// 具体命令
public class ConcreteCommand implements Command {
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.action();
}
}
// 接收者
public class Receiver {
public void action() {
System.out.println("Receiver action executed");
}
}
// 调用者
public class Invoker {
private Command command;
public Invoker(Command command) {
this.command = command;
}
public void invoke() {
command
.execute();
}
}
解释
Command
是命令接口,定义了执行请求的方法。ConcreteCommand
是具体的命令类,它将请求委托给接收者执行。Receiver
是命令的执行者,处理实际的操作。Invoker
是请求的发起者,持有一个命令对象,并在适当的时候调用其 execute
方法。
应用场景
- 远程控制:智能家居系统中,通过命令模式来控制家电设备的开关。
- 事务管理:数据库事务操作可以通过命令模式来组织,保证操作的回滚与恢复。
优缺点
- 优点:通过将请求封装成命令对象,使得请求的发送者和接收者解耦,支持撤销操作、命令队列等功能。
6. 备忘录模式(Memento Pattern)
定义
备忘录模式允许对象在不暴露其内部结构的情况下,保存和恢复其状态。它通常用于实现撤销操作功能,允许系统保存对象的状态,并在需要时恢复到某个之前的状态。
代码示例
// 备忘录类
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// 发起人类
public 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();
}
}
// 管理者类
public class Caretaker {
private List<Memento> mementoList = new ArrayList<>();
public void add(Memento memento) {
mementoList.add(memento);
}
public Memento get(int index) {
return mementoList.get(index);
}
}
解释
Originator
类是实际的业务对象,它负责设置和获取自己的状态,以及通过备忘录对象保存和恢复状态。Caretaker
类负责管理备忘录对象,但它不直接操作备忘录的内容,只负责保存和获取。
应用场景
- 文本编辑器中的撤销操作:当用户进行一系列编辑操作时,系统可以记录每一步操作的状态,以便用户点击撤销时恢复到先前的状态。
- 游戏存档功能:游戏中的进度可以通过备忘录模式来保存,玩家可以在不同的时间点加载游戏存档恢复进度。
优缺点
- 优点:提供了对对象状态的恢复能力,不需要暴露对象的内部实现。
- 缺点:如果需要保存的状态较多,可能会导致内存开销较大,且管理备忘录的过程可能比较复杂。
7. 访问者模式(Visitor Pattern)
定义
访问者模式允许在不改变元素类的前提下,定义作用于这些元素的操作。通过将操作封装成一个访问者对象,访问者模式可以在元素类层次结构上新增功能,而不改变现有类的代码。
代码示例
// 元素接口
public interface Element {
void accept(Visitor visitor);
}
// 具体元素A
public class ConcreteElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 具体元素B
public class ConcreteElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 访问者接口
public interface Visitor {
void visit(ConcreteElementA elementA);
void visit(ConcreteElementB elementB);
}
// 具体访问者
public class ConcreteVisitor implements Visitor {
@Override
public void visit(ConcreteElementA elementA) {
System.out.println("Visiting ConcreteElementA");
}
@Override
public void visit(ConcreteElementB elementB) {
System.out.println("Visiting ConcreteElementB");
}
}
解释
Element
接口是元素类的基类,它定义了接受访问者的方法 accept
。ConcreteElementA
和 ConcreteElementB
是具体的元素类,它们实现了 accept
方法,接受访问者的访问。Visitor
接口定义了针对每个元素类的访问方法,ConcreteVisitor
是具体的访问者,它实现了这些访问方法,执行相应的操作。
应用场景
- 图形渲染系统:不同类型的图形元素(如矩形、圆形等)可以通过访问者模式来执行各种操作,如渲染、计算面积等。
- 编译器设计:编译器可以通过访问者模式来实现对不同语法树节点的遍历和分析。
优缺点
- 优点:可以在不修改元素类的情况下增加新的操作,符合开闭原则,增强了系统的可扩展性。
- 缺点:访问者模式的设计会增加系统的复杂度,尤其是在元素类层次结构较多时,访问者的实现可能变得繁琐。
8. 中介者模式(Mediator Pattern)
定义
中介者模式用来定义一个中介对象,它通过中介来封装一系列的对象交互,使得对象之间不需要显式地相互引用,从而减少系统中的耦合性。
代码示例
// 同事接口
public interface Colleague {
void setMediator(Mediator mediator);
void send(String message);
void receive(String message);
}
// 具体同事A
public class ConcreteColleagueA implements Colleague {
private Mediator mediator;
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
@Override
public void send(String message) {
mediator.send(message, this);
}
@Override
public void receive(String message) {
System.out.println("Colleague A received: " + message);
}
}
// 具体同事B
public class ConcreteColleagueB implements Colleague {
private Mediator mediator;
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
@Override
public void send(String message) {
mediator.send(message, this);
}
@Override
public void receive(String message) {
System.out.println("Colleague B received: " + message);
}
}
// 中介者接口
public interface Mediator {
void send(String message, Colleague colleague);
}
// 具体中介者
public class ConcreteMediator implements Mediator {
private ConcreteColleagueA colleagueA;
private ConcreteColleagueB colleagueB;
public ConcreteMediator() {
colleagueA = new ConcreteColleagueA();
colleagueB = new ConcreteColleagueB();
colleagueA.setMediator(this);
colleagueB.setMediator(this);
}
@Override
public void send(String message, Colleague colleague) {
if (colleague == colleagueA) {
colleagueB.receive(message);
} else {
colleagueA.receive(message);
}
}
}
解释
Colleague
接口定义了所有同事对象的行为,ConcreteColleagueA
和 ConcreteColleagueB
是具体的同事类,它们通过中介者对象来发送和接收消息。Mediator
接口定义了中介者的行为,ConcreteMediator
是具体的中介者,它负责协调同事之间的交互。
应用场景
- GUI组件的事件处理:多个组件之间的交互可以通过中介者来协调,避免了组件间的直接通信。
- 聊天系统:用户之间的消息传递可以通过中介者来统一管理。
优缺点
- 优点:减少了对象之间的直接依赖,降低了耦合性,使得系统更加灵活。
- 缺点:中介者可能会变得过于庞大,承担过多的职责,导致系统难以维护。
9. 解释器模式(Interpreter Pattern)
定义
解释器模式用于定义一种语言的语法规则,并提供一个解释器,用来解释这些语法规则。该模式适用于需要对一个特定的语言或格式进行解释的场景。
代码示例
// 抽象表达式
public interface Expression {
boolean interpret(String context);
}
// 终结符表达式
public class TerminalExpression implements Expression {
private String data;
public TerminalExpression(String data) {
this.data = data;
}
@Override
public boolean interpret(String context) {
return context.contains(data);
}
}
// 非终结符表达式
public class OrExpression implements Expression {
private Expression expr1;
private Expression expr2;
public OrExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) || expr2.interpret(context);
}
}
解释
Expression
是抽象的表达式接口,定义了解释方法 interpret
。TerminalExpression
是具体的终结符表达式类,它用来处理简单的语法规则(如某个特定的字符串匹配)。OrExpression
是非终结符表达式类,它处理更复杂的语法规则(如组合条件的匹配)。
应用场景
- 正则表达式引擎:正则表达式的解释可以通过解释器模式来实现。
- 数学表达式解析:计算器的表达式解析可以使用解释器模式。
优缺点
- 优点:解释器模式易于扩展和维护,可以方便地增加新的语法规则。
- 缺点:对于复杂语法规则,可能导致性能问题并增加代码复杂性。
10. 状态模式(State Pattern)
定义
状态模式允许对象在其内部状态改变时,改变其行为。对象会根据自己的状态而表现出不同的行为,而无需显式地改变对象的类。状态模式主要用于解决那些在不同状态下具有不同行为的场景。
代码示例
// 状态接口
public interface State {
void handle(Context context);
}
// 具体状态A
public class ConcreteStateA implements State {
@Override
public void handle(Context context) {
System.out.println("Handling request in State A");
context.setState(new ConcreteStateB());
}
}
// 具体状态B
public class ConcreteStateB implements State {
@Override
public void handle(Context context) {
System.out.println("Handling request in State B");
context.setState(new ConcreteStateA());
}
}
// 上下文类
public class Context {
private State state;
public Context() {
state = new ConcreteStateA(); // 默认初始状态
}
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle(this);
}
}
解释
ConcreteStateA
和 ConcreteStateB
是具体的状态类,它们实现了 State
接口,定义了在不同状态下的行为。Context
类是上下文对象,它持有当前状态,并通过状态对象委托请求的处理。
应用场景
- 工作流管理系统:当系统的行为在不同的工作流程状态下不同,使用状态模式可以非常方便地切换和管理状态。
- 游戏中的角色状态管理:如角色的不同状态(例如静止、跑步、攻击等)下有不同的行为和操作。
优缺点
- 优点:可以避免多重的条件判断,清晰地分离每个状态的行为,符合开闭原则,易于扩展。
11. 模板方法模式(Template Method Pattern)
定义
模板方法模式定义了一个算法的框架,允许子类实现某些步骤的具体实现。模板方法模式通常通过父类来定义一个基本的算法结构,而在子类中实现算法的细节部分。
代码示例
// 抽象类
public abstract class AbstractClass {
// 模板方法
public void templateMethod() {
step1();
step2();
step3();
}
// 基本方法,由子类实现
protected abstract void step1();
protected abstract void step2();
protected void step3() {
System.out.println("Default implementation of step3");
}
}
// 具体类A
public class ConcreteClassA extends AbstractClass {
@Override
protected void step1() {
System.out.println("ConcreteClassA: Implementing step1");
}
@Override
protected void step2() {
System.out.println("ConcreteClassA: Implementing step2");
}
}
// 具体类B
public class ConcreteClassB extends AbstractClass {
@Override
protected void step1() {
System.out.println("ConcreteClassB: Implementing step1");
}
@Override
protected void step2() {
System.out.println("ConcreteClassB: Implementing step2");
}
@Override
protected void step3() {
System.out.println("ConcreteClassB: Overriding step3");
}
}
解释
AbstractClass
类定义了算法的模板方法 templateMethod
,它包含了一个固定的算法步骤顺序,部分步骤交由子类实现。ConcreteClassA
和 ConcreteClassB
是具体的子类,它们实现了部分步骤(如 step1
和 step2
),并且可以选择性地重写 step3
。
应用场景
- 数据处理流程:当多个类有相似的处理流程,只是其中某些步骤不同,可以使用模板方法模式来共享相同的算法流程。
- 代码生成工具:如代码生成器通常有固定的流程,部分步骤可以根据不同需求进行实现。
优缺点
- 优点:复用父类中通用的算法结构,避免代码重复;子类可以灵活地定制个别步骤的实现。
- 缺点:如果模板方法过于复杂,可能导致代码难以理解和维护。
12. 迭代器模式(Iterator Pattern)
定义
迭代器模式提供一种方式来访问集合对象的元素,而不暴露该集合的内部结构。它通过创建迭代器来遍历集合中的元素,从而实现集合元素的遍历功能。
代码示例
// 迭代器接口
public interface Iterator {
boolean hasNext();
Object next();
}
// 集合接口
public interface IterableCollection {
Iterator createIterator();
}
// 具体集合类
public class ConcreteCollection implements IterableCollection {
private List<Object> items = new ArrayList<>();
public void add(Object item) {
items.add(item);
}
@Override
public Iterator createIterator() {
return new ConcreteIterator(this);
}
public List<Object> getItems() {
return items;
}
}
// 具体迭代器类
public class ConcreteIterator implements Iterator {
private ConcreteCollection collection;
private int index;
public ConcreteIterator(ConcreteCollection collection) {
this.collection = collection;
}
@Override
public boolean hasNext() {
return index < collection.getItems().size();
}
@Override
public Object next() {
return collection.getItems().get(index++);
}
}
解释
Iterator
接口定义了遍历集合的方法,如 hasNext()
和 next()
。IterableCollection
接口定义了返回迭代器的工厂方法 createIterator
。ConcreteCollection
类是具体的集合类,它实现了 IterableCollection
接口并提供实际的数据集合。ConcreteIterator
是具体的迭代器类,负责遍历集合中的元素。
应用场景
- 容器类的遍历:当需要遍历复杂的数据结构(如链表、树、图等)时,使用迭代器模式可以统一遍历方式。
- 数据库查询结果的遍历:对于数据库查询结果集,可以使用迭代器模式来处理遍历和访问结果。
优缺点
- 优点:简化了集合的遍历操作,不暴露集合的内部结构;多个迭代器可以并发操作同一个集合。
- 缺点:如果集合对象发生变化,迭代器可能会变得无效,或者需要重新设计迭代器。
13. 责任链模式(Chain of Responsibility Pattern)
定义
责任链模式允许将多个处理对象按顺序链接起来,当请求发生时,责任链中的每个对象都可以处理该请求,或者将其传递给链中的下一个对象。这样可以避免请求的发送者和接收者之间的直接耦合。
代码示例
// 处理者抽象类
public abstract class Handler {
protected Handler nextHandler;
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public abstract void handleRequest(String request);
}
// 具体处理者A
public class ConcreteHandlerA extends Handler {
@Override
public void handleRequest(String request) {
if (request.equals("A")) {
System.out.println("Handled by ConcreteHandlerA");
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
// 具体处理者B
public class ConcreteHandlerB extends Handler {
@Override
public void handleRequest(String request) {
if (request.equals("B")) {
System.out.println("Handled by ConcreteHandlerB");
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
解释
Handler
抽象类定义了处理请求的 handleRequest
方法,并包含一个指向下一个处理者的引用。ConcreteHandlerA
和 ConcreteHandlerB
是具体的处理者类,它们实现了 handleRequest
方法来处理请求。如果当前处理者无法处理请求,它将把请求转发给链中的下一个处理者。
应用场景
- 日志处理:当请求到达时,多个处理者(如数据库日志、文件日志、控制台日志等)可以依次处理请求。
- 事件处理:用户事件(如点击、拖拽等)可以通过多个事件处理器传递,每个处理器可以根据不同的条件进行处理。
优缺点
- 优点:通过链式结构避免了请求发送者与处理者之间的紧耦合;每个处理者只关注自身的逻辑,易于扩展。
- 缺点:责任链的处理顺序可能不易控制,导致处理流程的复杂度增加。
总结
设计模式在软件开发中起到了至关重要的作用,它们为我们提供了通用的解决方案,以应对各种常见的编程挑战。通过这些模式,我们可以提高代码的复用性、可维护性和灵活性。在实际开发中,选择合适的设计模式可以有效地减少重复代码、避免冗余逻辑,并提高系统的可扩展性和可读性。
然而,设计模式并非一成不变的,它们并不是解决所有问题的万能钥匙。我们应该根据具体的业务需求、系统架构以及团队的经验来判断是否采用某个设计模式。了解并掌握这些设计模式,将帮助开发人员在面对复杂系统时做出更加高效且合理的设计决策。
最后,设计模式的学习和实践需要不断积累经验,通过实际项目的应用,才能更好地理解它们的优势与局限,进而在日后的工作中更加得心应手。
1⭐️ 好书推荐
《从零学JAVA设计模式》
在这里插入图片描述【内容简介】
本书以实用的设计模式为例,讲解了Java近年来在改善语法方面取得的进展,同时在实现这些模式的过程中展示了语言特性、设计模式与平台效率之间的关系。本书涵盖23种传统的设计模式、11种较为常见的模式,以及8种适用于多线程环境的并发模式。另外,本书还简要介绍了15种反模式。作者讲解这些模式的时候不仅给出了简洁、直观的范例代码,而且还利用UML类图与JFR工具展示了运用该模式的程序所具备的架构及运行细节,让读者能够更全面地了解模式对代码结构的影响,以及模式与Java虚拟机的交互情况。