设计模式
后面相关设计模式会在专栏中持续更新……
1、简单工厂模式
简单工厂模式是一种创建型设计模式,它提供了一种通过一个工厂类来创建同一类型的不同产品对象的方法。
在简单工厂模式中,我们首先定义一个抽象基类或者接口,它代表着所有可能被创建的对象的共性。然后我们编写具体的子类实现这个接口,并实现各自的构造函数和其他成员函数。最后,我们编写一个工厂类,根据客户端传递过来的参数,决定创建哪一个具体的对象子类,并返回该对象的指针或引用给客户端。
以下是一个简单的 C++ 示例代码:
// 抽象基类或接口
class Product {
public:
virtual void use() = 0;
};
// 具体产品子类之一
class ConcreteProductA : public Product {
public:
void use() override {
std::cout << "使用具体产品 A" << std::endl;
}
};
// 具体产品子类之二
class ConcreteProductB : public Product {
public:
void use() override {
std::cout << "使用具体产品 B" << std::endl;
}
};
// 工厂类
class SimpleFactory {
public:
static Product* createProduct(char type) {
switch (type) {
case 'A':
return new ConcreteProductA();
case 'B':
return new ConcreteProductB();
default:
return nullptr;
}
}
};
int main() {
// 客户端调用
Product* productA = SimpleFactory::createProduct('A');
Product* productB = SimpleFactory::createProduct('B');
productA->use();
productB->use();
delete productA;
delete productB;
return 0;
}
在这个示例中,Product 类是抽象基类或者接口,该类定义了所有可能被创建的对象的共性。然后我们定义了两个具体产品子类:ConcreteProductA 和 ConcreteProductB,它们继承自 Product 类,并实现了各自的构造函数和其他成员函数。
接下来,我们编写了一个名为 SimpleFactory 的工厂类。该工厂类提供了一个静态方法 createProduct,该方法根据传入的参数(即产品类型),决定创建哪一个具体的产品子类,并返回该对象的指针或引用给客户端。
最后,我们在客户端代码中调用 SimpleFactory 类的 createProduct 方法,根据需要创建不同的产品对象,调用其对应的成员函数进行使用。
简单工厂模式的优势在于可以将对象创建过程封装起来,避免客户端直接与具体的产品子类耦合在一起,从而提高代码的复用性和可维护性。
2、策略模式
策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。策略模式让算法独立于使用它的客户端而独立变化。
在策略模式中,我们首先定义一个抽象基类或接口,它代表着所有可能被应用的算法的共性。然后我们编写具体的子类实现这个接口,并实现各自的算法函数。最后,我们编写一个环境类或者上下文类,该类持有一个指向抽象基类或接口类型的指针,客户端可以传递不同的具体算法对象给环境类,从而动态地改变其行为。
以下是一个简单的 C++ 示例代码:
// 策略接口
class Strategy {
public:
virtual void execute() = 0;
};
// 具体策略之一
class ConcreteStrategyA : public Strategy {
public:
void execute() override {
std::cout << "执行策略 A" << std::endl;
}
};
// 具体策略之二
class ConcreteStrategyB : public Strategy {
public:
void execute() override {
std::cout << "执行策略 B" << std::endl;
}
};
// 上下文类
class Context {
public:
Context(Strategy* strategy) : m_strategy(strategy) {}
void setStrategy(Strategy* strategy) {
m_strategy = strategy;
}
void executeStrategy() {
m_strategy->execute();
}
private:
Strategy* m_strategy;
};
int main() {
// 客户端调用
Context context(new ConcreteStrategyA());
context.executeStrategy();
context.setStrategy(new ConcreteStrategyB());
context.executeStrategy();
return 0;
}
在这个示例中,Strategy 类是策略的抽象基类或者接口,该类定义了所有可能被应用的算法的共性。然后我们定义了两个具体策略子类:ConcreteStrategyA 和 ConcreteStrategyB,它们继承自 Strategy 类,并实现了各自的算法函数。
接下来,我们编写了一个名为 Context 的上下文类。该类持有一个指向抽象基类或接口类型的指针,客户端可以传递不同的具体算法对象给环境类,从而动态地改变其行为。上下文类有一个 executeStrategy 方法,该方法会调用持有的策略对象的 execute 函数来执行具体算法。
最后,我们在客户端代码中创建了一个 Context 对象,并传入一个具体策略子类对象(ConcreteStrategyA()),然后调用 executeStrategy 方法执行此策略算法。之后,我们调用 setStrategy 方法传入另一个具体策略子类对象(ConcreteStrategyB()),并再次调用 executeStrategy 方法来执行新的策略算法。
策略模式的优势在于可以将算法的实现和使用分离,方便客户端动态地替换策略对象,并且可以避免使用多重条件语句来选择不同的算法实现。
3、单一职责模式
单一职责模式是一种设计原则,它建议将一个类或者对象设计成只负责一项职责。这样可以使得类或对象的设计更加清晰简单,并且易于扩展和维护。
在单一职责模式中,我们通常需要对类或者对象进行拆分,将其不同的职责分别封装到不同的类或对象中。这样每个类或对象只需要关注自己的职责,而不需要处理其他的业务逻辑。
以下是一个简单的 C++ 示例代码:
#include <iostream>
// 打印机类
class Printer {
public:
void print(std::string text) {
std::cout << "打印:" << text << std::endl;
}
};
// 文件读取器类
class FileReader {
public:
std::string read(std::string filename) {
std::cout << "读取文件:" << filename << std::endl;
// 省略具体实现
return "";
}
};
// 客户端调用
int main() {
FileReader reader;
Printer printer;
std::string filename = "test.txt";
std::string content = reader.read(filename);
printer.print(content);
return 0;
}
在这个示例中,我们使用了单一职责模式来拆分 Printer 类和 FileReader 类。Printer 类只负责打印文本信息,而 FileReader 类只负责读取文件内容。这样每个类都有清晰的职责,易于扩展和维护。
在客户端代码中,我们首先创建了一个 FileReader 对象来读取文件内容,然后将读取到的文本内容传递给 Printer 对象进行打印。这样就实现了不同的功能职责分别由不同的类或对象负责处理。
单一职责模式的优势在于可以使得代码更加清晰简洁,易于理解和维护。同时也可以提高代码的可扩展性和可重用性。
4、装饰器模式
装饰器模式是一种结构型设计模式,它允许在不更改原有对象的情况下,动态地添加功能或者修改行为。
装饰器模式通过创建一个包装器(Wrapper)对象来实现对原有对象的增强。该包装器对象具有与原有对象相同的接口,可以通过组合的方式将多个包装器对象链接起来,形成一个链式结构,最后返回一个被多次增强后的对象。
以下是一个简单的 C++ 示例代码:
// 抽象组件类
class Component {
public:
virtual void operation() = 0;
};
// 具体组件类
class ConcreteComponent : public Component {
public:
void operation() override {
std::cout << "执行具体组件" << std::endl;
}
};
// 抽象装饰类
class Decorator : public Component {
public:
Decorator(Component* component) : m_component(component) {}
void operation() override {
if (m_component != nullptr) {
m_component->operation();
}
}
protected:
Component* m_component;
};
// 具体装饰类之一
class ConcreteDecoratorA : public Decorator {
public:
ConcreteDecoratorA(Component* component) : Decorator(component) {}
void operation() override {
addBehaviorA();
Decorator::operation();
}
private:
void addBehaviorA() {
std::cout << "为具体组件添加行为 A" << std::endl;
}
};
// 具体装饰类之二
class ConcreteDecoratorB : public Decorator {
public:
ConcreteDecoratorB(Component* component) : Decorator(component) {}
void operation() override {
addBehaviorB();
Decorator::operation();
}
private:
void addBehaviorB() {
std::cout << "为具体组件添加行为 B" << std::endl;
}
};
int main() {
// 客户端调用
Component* component = new ConcreteComponent();
Component* decoratorA = new ConcreteDecoratorA(component);
Component* decoratorB = new ConcreteDecoratorB(decoratorA);
decoratorB->operation();
delete decoratorB;
delete decoratorA;
delete component;
return 0;
}
在这个示例中,Component 是抽象组件类,它定义了所有可能被增强的对象的共性。然后我们定义了一个具体组件类 ConcreteComponent,该类实现了 Component 接口。
接下来,我们定义了一个抽象装饰类 Decorator,该类继承自 Component 类,并持有一个指向 Component 对象的指针,用于将其包装起来。Decorator 类中的 operation 函数会调用包装器持有的原有对象的 operation 函数。
然后,我们定义了两个具体装饰类:ConcreteDecoratorA 和 ConcreteDecoratorB,它们都继承自 Decorator 类,并分别实现了各自的增强行为函数,并在 operation 函数中调用增强函数。
最后,我们在客户端代码中创建一个 ConcreteComponent 对象作为被装饰对象,然后分别创建两个具体装饰类的对象 decoratorA 和 decoratorB,将其包装起来。最后调用链式结构中最后一个对象的 operation 函数即可完成被多次增强的操作。
装饰器模式的优势在于可以动态地添加或者移除对象的功能,避免了对原有代码进行修改或重构。同时也可以提高代码的复用性和灵活性。
5、单例模式
单例模式是一种创建型设计模式,它可以确保一个类在任何情况下都只有一个实例,并提供了一个全局访问该实例的接口。
单例模式通常需要保证以下几点:
- 确保只有一个实例。这意味着需要控制对象的创建和销毁,以及限制对象的数量。
- 提供全局访问点。这意味着需要提供一个静态方法或者变量来获取类的实例。
- 被多线程环境下正确地处理。这意味着需要考虑线程安全问题,避免多个线程同时访问或修改同一个实例。
以下是一个简单的 C++ 示例代码:
class Singleton {
public:
static Singleton* getInstance() {
static Singleton instance;
return &instance;
}
private:
Singleton() {} // 私有构造函数,禁止外部创建对象
Singleton(const Singleton& other); // 禁止拷贝构造函数
Singleton& operator=(const Singleton& other); // 禁止赋值运算符
};
int main() {
// 客户端调用
Singleton* instance1 = Singleton::getInstance();
Singleton* instance2 = Singleton::getInstance();
std::cout << (instance1 == instance2) << std::endl; // 输出 true
return 0;
}
在这个示例中,Singleton 是单例类,它只能被创建一次。我们使用了一个静态的 getInstance 函数来获取该单例对象。在函数中,我们使用了一个静态局部变量 instance 来确保只有一个实例,并返回它的地址给调用者。
由于构造函数、拷贝构造函数和赋值运算符都是私有的,因此无法从外部创建新的对象,只能通过 getInstance 函数获取单例对象。
最后,在客户端代码中,我们分别调用两次 getInstance 函数得到不同的指针,然后比较它们是否相等。结果会输出 true,说明两个指针指向的是同一个对象。
单例模式的优势在于可以确保一个类在任何情况下都只有一个实例并提供全局访问点,避免了重复创建对象和浪费内存空间。同时也可以提高代码的复用性和扩展性。
6、工厂方法模式
工厂方法模式是一种创建型设计模式,它提供了一种将对象创建过程封装在工厂中,并由工厂来决定要实例化哪个类的方式。
工厂方法模式可以分为两个部分:抽象工厂和具体工厂。抽象工厂定义了一个用于创建对象的接口,具体工厂则实现了该接口并负责创建特定类型的对象。
以下是一个简单的 C++ 示例代码:
#include <iostream>
#include <string>
// 抽象产品类
class Product {
public:
virtual ~Product() {}
virtual std::string getName() = 0;
};
// 具体产品类 A
class ConcreteProductA : public Product {
public:
std::string getName() override {
return "ConcreteProductA";
}
};
// 具体产品类 B
class ConcreteProductB : public Product {
public:
std::string getName() override {
return "ConcreteProductB";
}
};
// 抽象工厂类
class Factory {
public:
virtual ~Factory() {}
virtual Product* createProduct() = 0;
};
// 具体工厂类 A
class ConcreteFactoryA : public Factory {
public:
Product* createProduct() override {
return new ConcreteProductA();
}
};
// 具体工厂类 B
class ConcreteFactoryB : public Factory {
public:
Product* createProduct() override {
return new ConcreteProductB();
}
};
int main() {
// 客户端调用
Factory* factoryA = new ConcreteFactoryA();
Product* productA = factoryA->createProduct();
std::cout << productA->getName() << std::endl;
delete productA;
delete factoryA;
Factory* factoryB = new ConcreteFactoryB();
Product* productB = factoryB->createProduct();
std::cout << productB->getName() << std::endl;
delete productB;
delete factoryB;
return 0;
}
在这个示例中,Product 是抽象产品类,它定义了产品的共性。然后我们定义了两个具体产品类:ConcreteProductA 和 ConcreteProductB,分别实现了 Product 接口。
接下来,我们定义了抽象工厂类 Factory,它定义了用于创建产品对象的接口。然后我们定义了两个具体工厂类:ConcreteFactoryA 和 ConcreteFactoryB,分别实现了 Factory 接口,并负责创建特定类型的产品对象。
最后,在客户端代码中,我们分别创建 ConcreteFactoryA 和 ConcreteFactoryB 对象,并通过调用其 createProduct 方法来创建不同类型的产品对象。最终输出各自的产品名称。
工厂方法模式的优势在于可以将对象的创建过程与使用过程分离,并且可以轻易地扩展和替换对象的创建方式,同时也可以提高代码的复用性和可维护性。
7、抽象工厂模式
抽象工厂模式是一种创建型设计模式,它提供了一种将对象创建过程封装在工厂中,并由工厂来决定要实例化哪个类的方式。与工厂方法模式不同的是,抽象工厂模式可以创建一组相关或相互依赖的对象。
抽象工厂模式可以分为两个部分:抽象工厂和具体工厂。抽象工厂定义了一个用于创建对象的接口,具体工厂则实现了该接口并负责创建特定类型的对象。
以下是一个简单的 C++ 示例代码:
#include <iostream>
#include <string>
// 抽象产品 A 类
class AbstractProductA {
public:
virtual ~AbstractProductA() {}
virtual std::string getName() = 0;
};
// 具体产品 A1 类
class ProductA1 : public AbstractProductA {
public:
std::string getName() override {
return "ProductA1";
}
};
// 具体产品 A2 类
class ProductA2 : public AbstractProductA {
public:
std::string getName() override {
return "ProductA2";
}
};
// 抽象产品 B 类
class AbstractProductB {
public:
virtual ~AbstractProductB() {}
virtual std::string getName() = 0;
};
// 具体产品 B1 类
class ProductB1 : public AbstractProductB {
public:
std::string getName() override {
return "ProductB1";
}
};
// 具体产品 B2 类
class ProductB2 : public AbstractProductB {
public:
std::string getName() override {
return "ProductB2";
}
};
// 抽象工厂类
class AbstractFactory {
public:
virtual ~AbstractFactory() {}
virtual AbstractProductA* createProductA() = 0;
virtual AbstractProductB* createProductB() = 0;
};
// 具体工厂 1 类
class ConcreteFactory1 : public AbstractFactory {
public:
AbstractProductA* createProductA() override {
return new ProductA1();
}
AbstractProductB* createProductB() override {
return new ProductB1();
}
};
// 具体工厂 2 类
class ConcreteFactory2 : public AbstractFactory {
public:
AbstractProductA* createProductA() override {
return new ProductA2();
}
AbstractProductB* createProductB() override {
return new ProductB2();
}
};
int main() {
// 客户端调用
AbstractFactory* factory1 = new ConcreteFactory1();
AbstractProductA* productA1 = factory1->createProductA();
AbstractProductB* productB1 = factory1->createProductB();
std::cout << productA1->getName() << std::endl; // 输出 ProductA1
std::cout << productB1->getName() << std::endl; // 输出 ProductB1
delete productA1;
delete productB1;
delete factory1;
AbstractFactory* factory2 = new ConcreteFactory2();
AbstractProductA* productA2 = factory2->createProductA();
AbstractProductB* productB2 = factory2->createProductB();
std::cout << productA2->getName() << std::endl; // 输出 ProductA2
std::cout << productB2->getName() << std::endl; // 输出 ProductB2
delete productA2;
delete productB2;
delete factory2;
return 0;
}
在这个示例中,AbstractProductA 和 AbstractProductB 是抽象产品类,它们分别定义了一组产品的共性。
然后我们定义了两个具体产品类 ProductA1、ProductA2 和 ProductB1、ProductB2,分别实现了 AbstractProductA 和 AbstractProductB 接口。
接下来,我们定义了一个抽象工厂类 AbstractFactory,它定义了用于创建产品对象的接口。然后我们定义了两个具体工厂类 ConcreteFactory1、ConcreteFactory2,分别实现了 AbstractFactory 接口,并负责创建一组相关或相互依赖的产品。
最后,在客户端代码中,我们分别创建 ConcreteFactory1 和 ConcreteFactory2 对象,并通过调用其 createProductA 和 createProductB 方法来创建一组相关或相互依赖的产品对象。最终输出各自的产品名称。
抽象工厂模式的优势在于可以将对象的创建过程与使用过程分离,并且可以轻易地扩展和替换对象的创建方式,同时也可以提高代码的复用性和可维护性。它特别适用于要创建一组具有相同风格的对象时,例如创建不同操作系统下的图形界面或者不同主题的 UI 界面。但是抽象工厂模式的缺点也很明显,当需要增加新的产品族时,需要修改抽象工厂类和所有的具体工厂类。
8、命令模式
命令模式是一种行为型设计模式,它可以将请求封装成对象,从而允许我们使用不同的请求、队列或日志来参数化其他对象。同时也支持可撤销的操作。
命令模式中有四个角色:Command(抽象命令类)、ConcreteCommand(具体命令类)、Invoker(调用者类)和 Receiver(接收者类)。
以下是一个简单的 C++ 示例代码:
#include <iostream>
#include <vector>
// 抽象命令类
class Command {
public:
virtual ~Command() {}
virtual void execute() = 0;
virtual void undo() = 0;
};
// 具体命令类 1
class ConcreteCommand1 : public Command {
public:
ConcreteCommand1(Receiver* receiver) : m_receiver(receiver) {}
void execute() override {
m_receiver->action1();
}
void undo() override {
m_receiver->undo1();
}
private:
Receiver* m_receiver;
};
// 具体命令类 2
class ConcreteCommand2 : public Command {
public:
ConcreteCommand2(Receiver* receiver) : m_receiver(receiver) {}
void execute() override {
m_receiver->action2();
}
void undo() override {
m_receiver->undo2();
}
private:
Receiver* m_receiver;
};
// 接收者类
class Receiver {
public:
void action1() {
std::cout << "执行操作 1" << std::endl;
}
void undo1() {
std::cout << "撤销操作 1" << std::endl;
}
void action2() {
std::cout << "执行操作 2" << std::endl;
}
void undo2() {
std::cout << "撤销操作 2" << std::endl;
}
};
// 调用者类
class Invoker {
public:
~Invoker() {
for (auto command : m_commands) {
delete command;
}
m_commands.clear();
}
void setCommand(Command* command) {
m_commands.push_back(command);
}
void executeCommands() {
for (auto command : m_commands) {
command->execute();
}
}
void undoCommands() {
for (auto rit = m_commands.rbegin(); rit != m_commands.rend(); ++rit) {
(*rit)->undo();
}
}
private:
std::vector<Command*> m_commands;
};
int main() {
// 客户端调用
Receiver* receiver = new Receiver();
Command* command1 = new ConcreteCommand1(receiver);
Command* command2 = new ConcreteCommand2(receiver);
Invoker* invoker = new Invoker();
invoker->setCommand(command1);
invoker->setCommand(command2);
invoker->executeCommands(); // 执行命令
invoker->undoCommands(); // 撤销命令
delete invoker;
delete command1;
delete command2;
delete receiver;
return 0;
}
在这个示例中,Command 是抽象命令类,它定义了 execute 和 undo 方法。其中 execute 方法是执行命令的方法,undo 方法是撤销命令的方法。
然后我们定义了两个具体命令类 ConcreteCommand1 和 ConcreteCommand2,它们分别实现了 Command 接口,并且持有一个 Receiver 对象的引用。
接下来,我们定义了一个 Receiver 类,它实现了命令真正执行的操作。
最后,我们定义了一个 Invoker 类,它负责存储和执行一组命令。在客户端代码中,我们创建了一个 Receiver 对象和两个具体命令对象,然后将这些命令添加到调用者对象中。最终通过调用 executeCommands 方法和 undoCommands 方法分别执行和撤销命令。
命令模式的优势在于可以将请求与执行过程解耦,使得请求方不需要知道具体的执行过程,同时也可以支持可撤销的操作。但是命令模式也存在一些缺点,例如可能导致系统中出现过多的具体命令类,以及需要增加新的命令时,需要扩展和修改抽象命令类和所有的具体命令类。
另外,命令模式还有一些变种形式,例如宏命令、日志命令等。宏命令可以将多个命令组合成一个更大的命令,从而方便执行和撤销。日志命令则可以记录下每个命令的执行情况,并且支持重放操作。
9、外观模式
命令模式是一种行为型设计模式,它将请求封装成对象,从而允许我们使用不同的请求、队列或日志来参数化其他对象。同时也支持可撤销的操作。
命令模式中有四个角色:抽象命令类(Command)、具体命令类(ConcreteCommand)、调用者类(Invoker)和接收者类(Receiver)。
以下是一个简单的 C++ 示例代码:
#include <iostream>
#include <vector>
// 抽象命令类
class Command {
public:
virtual ~Command() {}
virtual void execute() = 0;
virtual void undo() = 0;
};
// 具体命令类 1
class ConcreteCommand1 : public Command {
public:
ConcreteCommand1(Receiver* receiver) : m_receiver(receiver) {}
void execute() override {
m_receiver->action1();
}
void undo() override {
m_receiver->undo1();
}
private:
Receiver* m_receiver;
};
// 具体命令类 2
class ConcreteCommand2 : public Command {
public:
ConcreteCommand2(Receiver* receiver) : m_receiver(receiver) {}
void execute() override {
m_receiver->action2();
}
void undo() override {
m_receiver->undo2();
}
private:
Receiver* m_receiver;
};
// 接收者类
class Receiver {
public:
void action1() {
std::cout << "执行操作 1" << std::endl;
}
void undo1() {
std::cout << "撤销操作 1" << std::endl;
}
void action2() {
std::cout << "执行操作 2" << std::endl;
}
void undo2() {
std::cout << "撤销操作 2" << std::endl;
}
};
// 调用者类
class Invoker {
public:
~Invoker() {
for (auto command : m_commands) {
delete command;
}
m_commands.clear();
}
void setCommand(Command* command) {
m_commands.push_back(command);
}
void executeCommands() {
for (auto command : m_commands) {
command->execute();
}
}
void undoCommands() {
for (auto rit = m_commands.rbegin(); rit != m_commands.rend(); ++rit) {
(*rit)->undo();
}
}
private:
std::vector<Command*> m_commands;
};
int main() {
// 客户端调用
Receiver* receiver = new Receiver();
Command* command1 = new ConcreteCommand1(receiver);
Command* command2 = new ConcreteCommand2(receiver);
Invoker* invoker = new Invoker();
invoker->setCommand(command1);
invoker->setCommand(command2);
invoker->executeCommands(); // 执行命令
invoker->undoCommands(); // 撤销命令
delete invoker;
delete command1;
delete command2;
delete receiver;
return 0;
}
在这个示例中,Command 是抽象命令类,它定义了 execute 和 undo 方法。其中 execute 方法是执行命令的方法,undo 方法是撤销命令的方法。
然后我们定义了两个具体命令类 ConcreteCommand1 和 ConcreteCommand2,它们分别实现了 Command 接口,并且持有一个 Receiver 对象的引用。
接下来,我们定义了一个 Receiver 类,它实现了命令真正执行的操作。
最后,我们定义了一个 Invoker 类,它负责存储和执行一组命令。在客户端代码中,我们创建了一个 Receiver 对象和两个具体命令对象,然后将这些命令添加到调用者对象中。最终通过调用 executeCommands 方法和 undoCommands 方法分别执行和撤销命令。
命令模式的优势在于可以将请求与执行过程解耦,使得请求方不需要知道具体的执行过程,同时也可以支持可撤销的操作。但是命令模式也存在一些缺点,例如可能导致系统中出现过多的具体命令类,以及需要增加新的命令时,需要扩展和修改抽象命令类和所有的具体命令类。
另外,命令模式还有一些变种形式,例如宏命令、日志命令等。宏命令可以将多个命令组合成一个更大的命令,从而方便执行和撤销。日志命令则可以记录下每个命令的执行情况,并且支持重放操作。
10、代理模式
代理模式是一种结构型设计模式,它提供了一种代理类来控制对另一个对象的访问。在某些情况下,客户端不能或不想直接引用一个对象,而是通过使用一个代理对象来间接访问该对象。
代理模式中有三个角色:抽象主题类(Subject)、具体主题类(RealSubject)和代理类(Proxy)。
以下是一个简单的 C++ 示例代码:
#include <iostream>
// 抽象主题类
class Subject {
public:
virtual ~Subject() {}
virtual void doSomething() = 0;
};
// 具体主题类
class RealSubject : public Subject {
public:
void doSomething() override {
std::cout << "真实主题执行操作" << std::endl;
}
};
// 代理类
class Proxy : public Subject {
public:
Proxy(Subject* subject) : m_subject(subject) {}
~Proxy() {
delete m_subject;
}
void doSomething() override {
std::cout << "代理类执行操作之前" << std::endl;
m_subject->doSomething();
std::cout << "代理类执行操作之后" << std::endl;
}
private:
Subject* m_subject;
};
int main() {
// 客户端调用
Subject* realSubject = new RealSubject();
Proxy* proxy = new Proxy(realSubject);
proxy->doSomething();
delete proxy;
return 0;
}
在这个示例中,Subject 是抽象主题类,它定义了 doSomething 方法。其中 doSomething 方法是主题类真正执行的操作。
然后我们定义了一个具体主题类 RealSubject,它实现了 Subject 接口,并且持有一个 Proxy 对象的引用。
接下来,我们定义了一个代理类 Proxy,它同样也实现了 Subject 接口,并且持有一个 Subject 对象的引用。在代理类中,它通过调用持有的主题对象的方法来完成实际的操作,并且也可以在方法调用前后进行一些其他的操作。
最后,在客户端代码中,我们创建了一个具体主题对象和一个代理对象,并且将具体主题对象传递给了代理对象。最终通过代理对象调用 doSomething() 方法来执行操作。
代理模式的优势在于可以提供额外的控制,例如在实际对象执行前后加入一些其他操作等。并且还可以实现远程代理、虚拟代理等不同的变种形式,以满足不同的需求。但是代理模式也可能会增加系统的复杂度,所以需要根据具体情况来选择使用。
设计原则
设计原则是指在设计软件系统时,遵循的一些基本原则、准则和约束条件。这些设计原则可以帮助开发人员设计出易于维护、可扩展性高、易于重用的软件系统。
以下是常见的几个设计原则:
单一职责原则(Single Responsibility Principle)
单一职责原则是指一个类或模块应该只有一个职责或功能,即一个类或模块只负责一项任务或功能。这样能够提高代码的可读性、可维护性和复用性。
例如,在一个学生管理系统中,应该将学生信息的存储、查询、修改等操作分别放在不同的类或模块中。
开闭原则(Open Closed Principle)
开闭原则是指一个模块或类对于扩展是开放的,但对于修改是关闭的。也就是说,当需要添加新功能时,应该通过增加代码来实现,而不是改变原有的代码。
例如,在一个图形绘制程序中,如果需要添加新的图形类型,应该通过继承已有的图形类来实现,而不是修改已有的图形类。
里氏替换原则(Liskov Substitution Principle)
里氏替换原则是指一个子类必须能够替换掉它的父类并且不会影响程序的正确性。也就是说,子类应该完全继承父类的行为,并且可以在不影响程序正确性的前提下扩展自己的行为。
例如,在一个汽车工厂中,轿车和卡车都是汽车的子类。如果要对汽车进行加速操作,那么轿车和卡车应该都能够实现这个操作,而不会出现不兼容的情况。
接口隔离原则(Interface Segregation Principle)
接口隔离原则是指一个类或模块不应该依赖于它不需要使用的接口。也就是说,应该将接口拆分成更小的、更具体的接口,以便让客户端只依赖于它需要使用的接口。
例如,在一个打印机类中,如果只需要支持打印功能,就不应该包含扫描、复印等其他功能的接口。
依赖倒置原则(Dependency Inversion Principle)
依赖倒置原则是指高层模块不应该依赖于底层模块,二者都应该依赖于抽象接口。也就是说,应该通过接口来解耦高层模块和底层模块之间的关系。
例如,在一个电视遥控器类中,如果需要控制多种类型的电视机,就应该定义一个电视机接口,并让具体的电视机实现这个接口。
总之,这些设计原则可以帮助开发人员设计出更加健壮、易于维护和扩展的软件系统。但是需要注意的是,不同的设计原则适用于不同的情况,需要根据具体的项目需求来选择合适的原则。而且这些原则也不是绝对的,需要在实践中