设计模式(Design Patterns)是软件开发中针对反复出现的问题总结出的通用解决方案。以下是23种经典设计模式中的常用类别及示例:
一、创建型模式(Creational Patterns)
关注点:对象的创建过程,解耦对象的使用和实例化。
1. 单例模式(Singleton)
确保一个类只有一个实例,并提供全局访问点。
class Logger {
private:
static Logger* instance;
Logger() = default;
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
public:
static Logger* getInstance() {
if (instance == nullptr) {
instance = new Logger();
}
return instance;
}
};
应用:配置管理器、日志系统。
2. 工厂模式(Factory Method)
定义创建对象的接口,让子类决定实例化哪个类。
class Product {
public:
virtual void operation() = 0;
};
class ConcreteProductA : public Product {
public:
void operation() override { /* ... */ }
};
class Creator {
public:
virtual Product* createProduct() = 0;
};
class ConcreteCreatorA : public Creator {
public:
Product* createProduct() override {
return new ConcreteProductA();
}
};
应用:游戏中的角色工厂、数据库连接工厂。
二、结构型模式(Structural Patterns)
关注点:如何将类或对象组合成更大的结构。
1. 装饰器模式(Decorator)
动态地给对象添加额外职责,替代继承。
class Component {
public:
virtual void operation() = 0;
};
class ConcreteComponent : public Component {
public:
void operation() override { /* 基础实现 */ }
};
class Decorator : public Component {
protected:
Component* component;
public:
Decorator(Component* c) : component(c) {}
void operation() override { component->operation(); }
};
class ConcreteDecoratorA : public Decorator {
public:
void operation() override {
Decorator::operation();
// 添加额外功能
}
};
应用:IO流包装(如BufferedInputStream
)、游戏角色装备系统。
2. 代理模式(Proxy)
为其他对象提供一种代理以控制对这个对象的访问。
class Subject {
public:
virtual void request() = 0;
};
class RealSubject : public Subject {
public:
void request() override { /* 实际操作 */ }
};
class Proxy : public Subject {
private:
RealSubject* realSubject;
public:
void request() override {
if (realSubject == nullptr) {
realSubject = new RealSubject();
}
// 访问控制或预处理
realSubject->request();
}
};
应用:远程代理(如RPC)、虚拟代理(延迟加载)。
三、行为型模式(Behavioral Patterns)
关注点:对象之间的通信和职责分配。
1. 观察者模式(Observer)
定义对象间的一对多依赖,当一个对象状态改变时,所有依赖者会收到通知并自动更新。
class Observer {
public:
virtual void update() = 0;
};
class Subject {
private:
std::vector<Observer*> observers;
public:
void attach(Observer* o) { observers.push_back(o); }
void detach(Observer* o) { /* ... */ }
void notify() {
for (auto o : observers) {
o->update();
}
}
};
应用:GUI事件处理、股票价格通知。
2. 策略模式(Strategy)
定义一系列算法,将每个算法封装起来,并使它们可以相互替换。
class Strategy {
public:
virtual void execute() = 0;
};
class ConcreteStrategyA : public Strategy {
public:
void execute() override { /* 具体算法A */ }
};
class Context {
private:
Strategy* strategy;
public:
void setStrategy(Strategy* s) { strategy = s; }
void executeStrategy() { strategy->execute(); }
};
应用:排序算法切换、支付方式选择。
3. 责任链模式(Chain of Responsibility)
将请求的发送和接收解耦,使多个对象都有机会处理这个请求。
class Handler {
protected:
Handler* successor;
public:
void setSuccessor(Handler* s) { successor = s; }
virtual void handleRequest() {
if (successor != nullptr) {
successor->handleRequest();
}
}
};
class ConcreteHandlerA : public Handler {
public:
void handleRequest() override {
// 处理部分请求或全部请求
if (/* 无法处理 */) {
Handler::handleRequest();
}
}
};
应用:异常处理链、权限验证链。
四、其他常用模式
1. 状态模式(State)
允许对象在内部状态改变时改变它的行为,看起来像是改变了它的类。
class State {
public:
virtual void handle() = 0;
};
class ConcreteStateA : public State {
public:
void handle() override { /* 状态A的行为 */ }
};
class Context {
private:
State* state;
public:
void setState(State* s) { state = s; }
void request() { state->handle(); }
};
应用:自动贩卖机状态管理、工作流引擎。
2. 适配器模式(Adapter)
将一个类的接口转换成客户希望的另一个接口,使不兼容的类可以一起工作。
class Target {
public:
virtual void request() = 0;
};
class Adaptee {
public:
void specificRequest() { /* 特殊请求 */ }
};
class Adapter : public Target {
private:
Adaptee* adaptee;
public:
void request() override {
adaptee->specificRequest();
}
};
应用:旧系统接口适配、第三方库包装。
五、设计模式的原则
- 单一职责原则:一个类只负责一个功能领域中的相应职责。
- 开闭原则:对扩展开放,对修改关闭。
- 里氏替换原则:子类可以替换父类而不影响程序的正确性。
- 依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖抽象。
- 接口隔离原则:客户端不应该依赖它不需要的接口。
- 迪米特法则:一个对象应该对其他对象保持最少的了解。
六、设计模式的应用场景
模式 | 适用场景 |
---|---|
单例模式 | 需要唯一实例的系统组件(如配置管理器、日志系统)。 |
工厂模式 | 对象创建逻辑复杂或需要根据条件动态创建对象。 |
观察者模式 | 状态变化需要通知多个对象的场景(如事件系统、发布-订阅)。 |
策略模式 | 算法频繁切换的场景(如支付方式、压缩算法)。 |
装饰器模式 | 需要动态添加功能而不影响原有类的场景(如IO流包装)。 |
总结
设计模式是解决软件设计问题的经验总结,合理使用可以提高代码的可维护性、可扩展性和复用性。在实际开发中,需根据具体需求选择合适的模式,避免过度设计。