一、设计模式的前世今生
在软件开发的早期,程序员们如同独自探索迷宫的行者,常常为相似的问题重复编写解决方案。直到 1994 年,Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著出版了一本名为 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素)的书,该书首次提到了软件开发中设计模式的概念。
这 23 种设计模式的提出,就像为程序员们绘制了一幅迷宫地图,让他们在面对相似问题时能够快速找到最佳解决方案。如今,设计模式已成为现代软件开发中不可或缺的一部分,广泛应用于各种 Java 框架和项目中。
二、设计模式的核心价值
设计模式的本质是一种经验的总结,它解决了软件开发中反复出现的问题。其核心价值主要体现在以下几个方面:
1. 提高软件的可维护性
当软件系统规模变大时,代码的可维护性就变得至关重要。设计模式通过将复杂的系统划分为多个职责明确的模块,使得代码结构清晰,易于理解和修改。例如,在一个电商系统中,使用 MVC 模式(Model-View-Controller)可以将业务逻辑、数据处理和界面展示分离,当需要修改界面时,不会影响到业务逻辑和数据处理部分。
2. 增强软件的可扩展性
软件系统需要不断适应需求的变化,设计模式通过提供灵活的架构,使得系统能够轻松应对各种扩展。例如,在一个游戏开发中,使用策略模式可以在不修改原有代码的情况下,轻松添加新的游戏角色或技能。
3. 实现软件的可复用性
设计模式鼓励代码的复用,通过将通用的解决方案封装成模式,可以在不同的项目中重复使用。例如,单例模式可以确保一个类只有一个实例,并提供全局访问点,这在很多场景下都非常有用,如数据库连接池、配置管理器等。
三、设计模式的分类
根据设计模式的用途,可以将其分为三大类:创建型模式、结构型模式和行为型模式。每一类都包含多种具体的设计模式,下面将详细介绍。
(一)创建型模式
创建型模式主要用于对象的创建过程,它隐藏了对象创建的细节,使得代码更加灵活和可维护。常见的创建型模式包括:
1. 单例模式(Singleton Pattern)
单例模式确保一个类只有一个实例,并提供一个全局访问点。在 Java 中,单例模式有多种实现方式,如饿汉式、懒汉式、双重检查锁等。
java
// 饿汉式单例模式
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
2. 工厂模式(Factory Pattern)
工厂模式定义了一个创建对象的接口,让子类决定实例化哪个类。工厂模式可以分为简单工厂模式、工厂方法模式和抽象工厂模式。
java
// 简单工厂模式
public class ShapeFactory {
public Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
return new Rectangle();
} else if (shapeType.equalsIgnoreCase("SQUARE")) {
return new Square();
}
return null;
}
}
3. 抽象工厂模式(Abstract Factory Pattern)
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
java
// 抽象工厂接口
public interface AbstractFactory {
Shape getShape(String shapeType);
Color getColor(String colorType);
}
// 具体工厂实现
public class ShapeFactory implements AbstractFactory {
@Override
public Shape getShape(String shapeType) {
// 实现略
}
@Override
public Color getColor(String colorType) {
return null;
}
}
4. 建造者模式(Builder Pattern)
建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
java
// 产品类
public class Computer {
private String cpu;
private String ram;
private String hardDisk;
// getter和setter方法
}
// 建造者接口
public interface ComputerBuilder {
void buildCPU();
void buildRAM();
void buildHardDisk();
Computer getComputer();
}
// 具体建造者
public class GamingComputerBuilder implements ComputerBuilder {
private Computer computer = new Computer();
@Override
public void buildCPU() {
computer.setCPU("Intel i9");
}
@Override
public void buildRAM() {
computer.setRAM("32GB");
}
@Override
public void buildHardDisk() {
computer.setHardDisk("1TB SSD");
}
@Override
public Computer getComputer() {
return computer;
}
}
5. 原型模式(Prototype Pattern)
原型模式通过复制现有对象来创建新对象,而无需知道创建的细节。
java
// 原型接口
public interface Prototype {
Prototype clone();
}
// 具体原型类
public class ConcretePrototype implements Prototype {
private int id;
private String name;
public ConcretePrototype(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public Prototype clone() {
return new ConcretePrototype(this.id, this.name);
}
}
(二)结构型模式
结构型模式主要用于处理类或对象的组合,它通过继承或组合的方式来实现新的功能。常见的结构型模式包括:
1. 代理模式(Proxy Pattern)
代理模式为其他对象提供一种代理以控制对这个对象的访问。
java
// 接口
public interface Image {
void display();
}
// 真实类
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName) {
System.out.println("Loading " + fileName);
}
}
// 代理类
public class ProxyImage implements Image {
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}
}
2. 适配器模式(Adapter Pattern)
适配器模式将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
java
// 目标接口
public interface Target {
void request();
}
// 适配者类
public class Adaptee {
public void specificRequest() {
System.out.println("Specific request");
}
}
// 适配器类
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
3. 装饰器模式(Decorator Pattern)
装饰器模式动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更为灵活。
java
// 组件接口
public interface Component {
void operation();
}
// 具体组件
public class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent operation");
}
}
// 装饰器抽象类
public abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰器
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
System.out.println("ConcreteDecoratorA added behavior");
}
}
4. 外观模式(Facade Pattern)
外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
java
// 子系统类
public class SubSystemOne {
public void methodOne() {
System.out.println("SubSystemOne methodOne");
}
}
public class SubSystemTwo {
public void methodTwo() {
System.out.println("SubSystemTwo methodTwo");
}
}
// 外观类
public class Facade {
private SubSystemOne one;
private SubSystemTwo two;
public Facade() {
one = new SubSystemOne();
two = new SubSystemTwo();
}
public void operationA() {
System.out.println("Operation A");
one.methodOne();
two.methodTwo();
}
public void operationB() {
System.out.println("Operation B");
two.methodTwo();
}
}
5. 桥接模式(Bridge Pattern)
桥接模式将抽象部分与它的实现部分分离,使它们都可以独立地变化。
java
// 实现接口
public interface Implementor {
void operationImpl();
}
// 具体实现类
public class ConcreteImplementorA implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorA operationImpl");
}
}
// 抽象类
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();
}
}
(三)行为型模式
行为型模式主要用于处理对象之间的交互和职责分配,它关注的是对象之间的通信和协作。常见的行为型模式包括:
1. 观察者模式(Observer Pattern)
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己的状态。
java
// 主题接口
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
// 观察者接口
public interface Observer {
void update(String message);
}
// 具体主题
public class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String message;
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
observers.remove(o);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(message);
}
}
public void setMessage(String message) {
this.message = message;
notifyObservers();
}
}
// 具体观察者
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);
}
}
2. 策略模式(Strategy Pattern)
策略模式定义了一系列的算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。
java
// 策略接口
public interface Strategy {
int doOperation(int num1, int num2);
}
// 具体策略
public class OperationAdd implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
public class OperationSubtract implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
// 上下文
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
}
3. 模板方法模式(Template Method Pattern)
模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
java
// 抽象类
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
// 模板方法
public final void play() {
initialize();
startPlay();
endPlay();
}
}
// 具体类
public class Cricket extends Game {
@Override
void initialize() {
System.out.println("Cricket Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
@Override
void endPlay() {
System.out.println("Cricket Game Finished!");
}
}
4. 责任链模式(Chain of Responsibility Pattern)
责任链模式为请求创建了一个接收者对象的链,每个接收者都包含对另一个接收者的引用,如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
java
// 抽象处理者
public abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public abstract void handleRequest(Request request);
}
// 具体处理者
public class ConcreteHandler1 extends Handler {
@Override
public void handleRequest(Request request) {
if (request.getType() == RequestType.TYPE1) {
System.out.println("ConcreteHandler1 handling request: " + request.getDescription());
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
public class ConcreteHandler2 extends Handler {
@Override
public void handleRequest(Request request) {
if (request.getType() == RequestType.TYPE2) {
System.out.println("ConcreteHandler2 handling request: " + request.getDescription());
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
// 请求类
public class Request {
private RequestType type;
private String description;
public Request(RequestType type, String description) {
this.type = type;
this.description = description;
}
public RequestType getType() {
return type;
}
public String getDescription() {
return description;
}
}
// 请求类型枚举
public enum RequestType {
TYPE1, TYPE2
}
5. 状态模式(State Pattern)
状态模式允许对象在内部状态发生改变时改变它的行为,对象看起来似乎修改了它的类。
java
// 状态接口
public interface State {
void doAction(Context context);
}
// 具体状态
public class StartState implements State {
@Override
public void doAction(Context context) {
System.out.println("Player is in start state");
context.setState(this);
}
@Override
public String toString() {
return "Start State";
}
}
public class StopState implements State {
@Override
public void doAction(Context context) {
System.out.println("Player is in stop state");
context.setState(this);
}
@Override
public String toString() {
return "Stop State";
}
}
// 上下文
public class Context {
private State state;
public Context() {
state = null;
}
public void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
}
四、设计模式的应用场景与注意事项
1. 应用场景
设计模式在以下场景中特别有用:
- 可维护性要求高的项目:大型项目中,合理使用设计模式可以使代码结构清晰,易于维护。
- 需要频繁扩展的项目:当项目需要不断添加新功能时,设计模式可以提供灵活的扩展机制。
- 团队协作开发:设计模式是一种通用的语言,可以帮助团队成员更好地沟通和理解彼此的代码。
2. 注意事项
在使用设计模式时,需要注意以下几点:
- 不要过度设计:设计模式是为了解决问题,而不是为了使用而使用。过度使用设计模式会导致代码复杂化。
- 理解问题本质:在选择设计模式之前,需要深入理解问题的本质,选择最合适的模式。
- 结合实际情况:设计模式不是一成不变的,需要根据实际情况进行调整和优化。
五、总结
设计模式是软件开发中的宝贵财富,它提供了一套通用的解决方案,帮助我们解决软件开发中反复出现的问题。通过合理使用设计模式,可以提高软件的可维护性、可扩展性和可复用性。
本文介绍了 Java 中常见的 23 种设计模式,包括创建型模式、结构型模式和行为型模式,并通过示例代码展示了它们的实现方式。在实际开发中,我们需要根据具体情况选择合适的设计模式,避免过度设计,以达到最佳的开发效果。
掌握设计模式不仅是成为一名优秀程序员的必备技能,更是提升软件开发能力的重要途径。希望本文能够帮助读者更好地理解和应用设计模式,在软件开发的道路上不断前进。