C++设计的七大原则
1、单一职责原则
对于类来说,就是一个类应该只负责一项职责,如果类A负责了职责1和职责2,当职责1的需求发生改变时,可能对职责2的执行造成影响,因此需要将类A分解为类A1和类A2。
2、接口隔离原则
接口隔离原则(Interface Segregation Principle,ISP)是面向对象编程中的一个设计原则,它强调“客户端不应该被迫依赖于它不使用的接口”。该原则的核心思想是将一个大的接口拆分成多个更小、更具体的接口,以避免客户端依赖不必要的接口方法。
在面向对象编程中,接口是一种约定,它定义了类或对象应该提供的方法和属性。然而,有时候一个接口会变得臃肿,包含了许多对于某些类并非必需的方法,这可能导致以下问题:
- 不必要的依赖: 如果一个类实现了一个庞大的接口,但实际上只需要使用其中的一部分方法,那么它就不得不依赖于不需要的方法,这会增加代码的复杂性并降低代码的可维护性。
- 修改的困难性: 如果一个接口发生变化,那么实现这个接口的所有类都需要进行相应的修改。当一个接口包含了很多方法时,即使只对一个方法做了修改,也可能会影响到许多类,增加了维护的难度。
接口隔离原则的解决方法是将大的接口拆分成多个小的接口,每个小接口代表一个特定的行为或角色。这样,类只需要实现与其相关的接口,避免了不必要的依赖,也降低了修改的影响范围。这有助于提高代码的可扩展性、可维护性和可重用性。
总之,接口隔离原则强调设计接口时要精心考虑接口的粒度,确保接口具有高内聚性(即相关功能放在一起),并尽量避免接口的冗余和不必要的依赖。
3、依赖倒转原则
- 高层模块与低层模块分离: 高层模块和低层模块之间不应该直接相互依赖。而是通过抽象接口或类来定义它们之间的通信方式,从而降低耦合。
- 抽象不依赖于细节: 抽象(通常是接口或抽象类)应该定义模块的约定,而具体的实现细节应该遵循这些约定。这就是为什么说抽象不应该依赖于具体实现。
- 反转控制: 控制流程的控制权被反转,通常由框架或容器来管理模块之间的依赖关系,而不是模块自己管理依赖。这有助于减少代码中的硬编码依赖。
总之,依赖倒转原则强调了通过抽象来分离高层模块和低层模块之间的依赖关系,从而实现松耦合的设计,提高代码的可维护性和可扩展性。
4、里氏替换原则
- 子类必须能够替代父类: 子类对象应该具有与父类对象一致的行为,包括方法签名、预期的输入输出以及约定的语义。这确保了子类可以在不破坏程序正确性的情况下替代父类。
- 不改变父类行为: 子类可以通过扩展或重写父类的方法来实现新的功能,但不能改变父类已有方法的行为。子类应该保持父类的基本行为,以确保替代时不会引起意外的后果。
- 避免抛出异常: 如果父类中的方法在特定情况下会抛出异常,那么子类重写这些方法时也应该遵循相同的异常规则。子类不能抛出比父类更宽泛的异常类型,否则会破坏替代性。
遵循里氏替换原则可以提高代码的可维护性、可扩展性和可重用性。当我们在使用继承关系时,确保子类可以安全地替代父类,不会引发意外错误,从而保持程序的正确性和稳定性。
需要注意的是,虽然里氏替换原则是一个重要的设计原则,但并不是所有情况下都适合使用继承。有时候,组合、接口实现等其他设计方式可能更为合适,具体选择取决于情况。
5、开闭原则
- 开放性(Open for Extension): 软件实体应该允许通过新增代码来扩展其功能,以满足新的需求,而不需要修改已有的代码。
- 封闭性(Closed for Modification): 已有的代码在扩展时不应该被修改,这样可以保证现有功能的稳定性和正确性。新的功能应该通过扩展而不是修改来实现。
开闭原则的目标是减少系统的脆弱性,提高代码的可维护性和可扩展性。通过将变化的部分封装成可扩展的模块或组件,可以降低系统各部分之间的耦合,使得修改一个部分不会影响其他部分。
为了遵循开闭原则,可以使用以下几种方法:
- 使用抽象类和接口定义公共的行为,使得可以通过实现不同的子类来扩展功能。
- 使用多态性和动态绑定,使得可以在运行时替换对象的具体实现。
- 使用设计模式,如策略模式、装饰器模式等,来实现开闭原则。
总之,开闭原则是面向对象设计中的一个核心原则,它鼓励使用扩展而不是修改的方式来实现新功能,从而提高代码的灵活性和可维护性。
6、迪米特法则
在迪米特法则中,一个对象的“直接朋友”包括以下几种:
- 该对象本身。
- 以参数形式传入该对象方法的对象。
- 该对象所创建的对象。
- 该对象的成员对象。
迪米特法则强调了以下几个原则:
- 减少耦合: 一个类不应该知道太多关于其他类的内部结构和实现细节,从而减少类之间的耦合度。
- 封装隐私: 类的内部结构和实现细节应该被封装起来,只提供少量对外暴露的接口,以防止意外的相互依赖和影响。
- 局部通信: 对象之间的通信应该局限在有限的几个直接朋友之间,不应该通过中间对象来传递信息。
- 保持独立性: 类的修改不应该影响与之交互的其他类,从而实现类的独立性。
7、合成复用原则
该原则的核心思想是通过构建对象的组合关系,将已有的类和模块组合成新的类,从而实现功能的复用。相对于继承,合成复用具有更好的灵活性和适应性,因为继承会引入紧耦合的依赖关系,可能导致修改父类时对子类产生意外影响。
合成复用原则的主要特点包括:
- 优先使用组合: 在设计时,优先选择将已有的类组合在一起,形成新的对象,而不是通过继承来实现功能的扩展。
- 减少继承: 尽量避免使用继承来复用代码,因为继承可能引入不必要的依赖和耦合,降低系统的灵活性。
- 模块化设计: 将系统分解为各个独立的模块,通过合成这些模块来实现功能,从而降低模块之间的耦合。
- 松散耦合: 合成复用可以实现松耦合,使得组件可以独立演化和维护,减少修改一个组件对其他组件造成的影响。
通过遵循合成复用原则,可以创建更加灵活、可维护和可扩展的系统。当需要引入新的功能时,可以通过组合现有的模块来构建,而不是直接修改已有的类和模块。
总之,合成复用原则强调通过组合已有的类和模块来实现新功能,从而降低系统的耦合度,提高代码的可复用性和可维护性。
C++设计模式
一、设计模式的分类
总体来说设计模式分为三大类
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
创建型模式
1.工厂方法模式
根据工厂方法模式的不同实现方式,可以将其分为以下几个分类:
- 简单工厂模式(Simple Factory Pattern): 简单工厂模式并不是标准的工厂方法模式,但是它是工厂方法模式的一个特殊情况。在简单工厂模式中,通常有一个工厂类负责创建多种不同类型的对象,客户端只需要传入相应的参数,工厂类就会根据参数来创建对应的对象。虽然简单工厂模式不符合开闭原则,但在某些情况下可以简化对象的创建过程。以下是其代码举例:
#include <iostream>
// 基类 Shape,代表所有形状的抽象类
class Shape {
public:
virtual void draw() = 0;
};
// Circle 类,表示圆形
class Circle : public Shape {
public:
void draw() override {
std::cout << "Drawing a circle." << std::endl;
}
};
// Square 类,表示正方形
class Square : public Shape {
public:
void draw() override {
std::cout << "Drawing a square." << std::endl;
}
};
// 简单工厂类,根据传入的参数创建不同的对象
class ShapeFactory {
public:
Shape* createShape(int type) {
switch (type) {
case 0:
return new Circle();
case 1:
return new Square();
default:
return nullptr;
}
}
};
int main() {
ShapeFactory factory;
// 创建圆形
Shape* circle = factory.createShape(0);
if (circle) {
circle->draw();
delete circle;
}
// 创建正方形
Shape* square = factory.createShape(1);
if (square) {
square->draw();
delete square;
}
return 0;
}
- 普通工厂模式(Factory Method Pattern): 普通工厂模式是工厂方法模式的基本形式,它定义了一个抽象的工厂接口,然后由具体的子类来实现工厂接口中的方法,以创建具体的产品对象。每个具体的子类负责创建一类产品,从而将对象的创建与使用分离。
#include <iostream>
// 抽象产品类 Animal
class Animal {
public:
virtual void makeSound() = 0;
};
// 具体产品类 Dog
class Dog : public Animal {
public:
void makeSound() override {
std::cout << "Dog makes woof sound." << std::endl;
}
};
// 具体产品类 Cat
class Cat : public Animal {
public:
void makeSound() override {
std::cout << "Cat makes meow sound." << std::endl;
}
};
// 抽象工厂类 AnimalFactory
class AnimalFactory {
public:
virtual Animal* createAnimal() = 0;
};
// 具体工厂类 DogFactory
class DogFactory : public AnimalFactory {
public:
Animal* createAnimal() override {
return new Dog();
}
};
// 具体工厂类 CatFactory
class CatFactory : public AnimalFactory {
public:
Animal* createAnimal() override {
return new Cat();
}
};
int main() {
AnimalFactory* factory = new DogFactory();
Animal* dog = factory->createAnimal();
dog->makeSound();
delete dog;
delete factory;
factory = new CatFactory();
Animal* cat = factory->createAnimal();
cat->makeSound();
delete cat;
delete factory;
return 0;
}
- 抽象工厂模式(Abstract Factory Pattern): 抽象工厂模式是一种更高层次的工厂方法模式。在抽象工厂模式中,有一个抽象的工厂接口,然后有多个具体的工厂类实现该接口,每个工厂类负责创建一族(相关的)产品。这样可以创建一组相关的产品,而不仅仅是单个产品。
#include <iostream>
// 抽象产品类 Button
class Button {
public:
virtual void click() = 0;
};
// 具体产品类 WindowsButton
class WindowsButton : public Button {
public:
void click() override {
std::cout << "Windows button clicked." << std::endl;
}
};
// 具体产品类 LinuxButton
class LinuxButton : public Button {
public:
void click() override {
std::cout << "Linux button clicked." << std::endl;
}
};
// 抽象产品类 TextBox
class TextBox {
public:
virtual void input() = 0;
};
// 具体产品类 WindowsTextBox
class WindowsTextBox : public TextBox {
public:
void input() override {
std::cout << "Windows text box input." << std::endl;
}
};
// 具体产品类 LinuxTextBox
class LinuxTextBox : public TextBox {
public:
void input() override {
std::cout << "Linux text box input." << std::endl;
}
};
// 抽象工厂类 GUIFactory
class GUIFactory {
public:
virtual Button* createButton() = 0;
virtual TextBox* createTextBox() = 0;
};
// 具体工厂类 WindowsFactory
class WindowsFactory : public GUIFactory {
public:
Button* createButton() override {
return new WindowsButton();
}
TextBox* createTextBox() override {
return new WindowsTextBox();
}
};
// 具体工厂类 LinuxFactory
class LinuxFactory : public GUIFactory {
public:
Button* createButton() override {
return new LinuxButton();
}
TextBox* createTextBox() override {
return new LinuxTextBox();
}
};
int main() {
GUIFactory* windowsFactory = new WindowsFactory();
Button* windowsButton = windowsFactory->createButton();
TextBox* windowsTextBox = windowsFactory->createTextBox();
windowsButton->click();
windowsTextBox->input();
delete windowsButton;
delete windowsTextBox;
delete windowsFactory;
GUIFactory* linuxFactory = new LinuxFactory();
Button* linuxButton = linuxFactory->createButton();
TextBox* linuxTextBox = linuxFactory->createTextBox();
linuxButton->click();
linuxTextBox->input();
delete linuxButton;
delete linuxTextBox;
delete linuxFactory;
return 0;
}
2.单例模式
单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。这意味着在整个应用程序的生命周期内,只会存在一个特定的类实例。
单例模式通常在需要共享资源的情况下使用,例如配置信息、数据库连接池、线程池等。通过保证只有一个实例存在,可以避免资源重复创建和占用过多内存。
#include <iostream>
class Singleton {
private:
// 私有的构造函数,防止外部创建实例
Singleton() {
std::cout << "Singleton instance created." << std::endl;
}
// 私有的静态实例指针
static Singleton* instance;
public:
// 公共的获取实例方法
static Singleton* getInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
// 其他成员方法...
};
// 初始化静态成员变量
Singleton* Singleton::instance = nullptr;
int main() {
// 获取单例实例
Singleton* singleton1 = Singleton::getInstance();
Singleton* singleton2 = Singleton::getInstance();
// 判断是否为同一实例
if (singleton1 == singleton2) {
std::cout << "Both instances are the same." << std::endl;
} else {
std::cout << "Instances are different." << std::endl;
}
return 0;
}
在这个示例中,Singleton 类只允许创建一个实例。它的构造函数是私有的,所以外部无法直接创建实例。通过静态成员变量 instance 来存储实例,静态成员方法 getInstance() 用于获取该实例。如果实例不存在,则在首次调用 getInstance() 时创建实例,以后调用都返回相同的实例。
需要注意的是,这种简单的单例模式实现并不是线程安全的。在多线程环境中,可能会出现并发创建多个实例的情况。为了保证线程安全,可以使用加锁等机制,或者使用更为高级的单例实现方式(如双重检查锁定、静态内部类等)。
3.建造者模式
旨在通过将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。这种模式允许你通过一步一步的方式创建一个复杂的对象,同时避免构造函数参数列表的复杂性。
建造者模式通常适用于以下情况:
- 当一个对象有多个部分组成,且构建这些部分的过程相对复杂。
- 当需要创建多个具有不同组合的对象时。
- 当希望将对象的构建过程与其表示分离,以便在相同的构建过程下构建不同的表示。
#include <iostream>
#include <string>
class CPU {
public:
void setModel(const std::string& model) {
model_ = model;
}
private:
std::string model_;
};
class Memory {
public:
void setSize(int sizeGB) {
sizeGB_ = sizeGB;
}
private:
int sizeGB_;
};
class Storage {
public:
void setType(const std::string& type) {
type_ = type;
}
private:
std::string type_;
};
class ComputerBuilder {
public:
void buildCPU(const std::string& model) {
computer_.cpu.setModel(model);
}
void buildMemory(int sizeGB) {
computer_.memory.setSize(sizeGB);
}
void buildStorage(const std::string& type) {
computer_.storage.setType(type);
}
Computer getResult() {
return computer_;
}
private:
Computer computer_;
};
class Computer {
public:
void displayInfo() {
std::cout << "CPU: " << cpu.model_ << "\n";
std::cout << "Memory: " << memory.sizeGB_ << "GB\n";
std::cout << "Storage: " << storage.type_ << "\n";
}
private:
friend class ComputerBuilder;
CPU cpu;
Memory memory;
Storage storage;
};
int main() {
ComputerBuilder builder;
builder.buildCPU("Intel Core i7");
builder.buildMemory(16);
builder.buildStorage("SSD");
Computer computer = builder.getResult();
computer.displayInfo();
return 0;
}
在这个例子中,我们使用建造者模式创建了一个 Computer 对象。ComputerBuilder 负责构建 Computer 对象的各个部分,然后通过 getResult 方法获取最终的构建结果。这种方式可以灵活地组合不同的部分来创建不同配置的电脑。
在建造者模式中,ComputerBuilder 类负责构建 Computer 对象的各个部分,但这些部分的具体实现细节是在 Computer 类内部定义的。为了让 ComputerBuilder 类能够设置 Computer 对象的私有成员(如 CPU、Memory、Storage),需要在 Computer 类中声明 ComputerBuilder 为友元类。
具体来说,通过将 friend class ComputerBuilder; 放在 Computer 类的定义中,就允许 ComputerBuilder 类可以访问 Computer 类的私有成员。这使得在 ComputerBuilder 类中可以直接设置 Computer 对象的属性,而不需要通过公共接口或者访问器函数。
需要注意的是,使用友元关系会引入一些访问控制的弱点,因为友元类可以访问类的私有成员。在使用友元关系时,应该谨慎考虑设计和安全性的问题,以确保不会泄漏敏感信息或破坏封装性。在上述例子中,由于建造者模式需要访问 Computer 类的私有成员来设置属性,使用友元关系是一种合理的设计选择。
4.原型模式
它允许通过复制已有对象的实例来创建新的对象,而无需从头开始重新构建对象。这种模式可以在需要创建多个相似对象的情况下,减少重复的初始化工作,提高效率。
原型模式的核心思想是:通过克隆(复制)一个现有对象来创建新的对象。这个过程可以通过浅拷贝或深拷贝来实现,具体取决于对象中是否包含了其他引用类型的成员。
#include <iostream>
#include <string>
class Prototype {
public:
virtual Prototype* clone() = 0;
virtual void printInfo() = 0;
};
class ConcretePrototype : public Prototype {
private:
std::string info;
#include <iostream>
#include <string>
class Prototype {
public:
virtual Prototype* clone() = 0;
virtual void printInfo() = 0;
};
class ConcretePrototype : public Prototype {
private:
std::string info;
public:
ConcretePrototype(std::string info) : info(info) {}
Prototype* clone() override {
return new ConcretePrototype(*this);
}
void printInfo() override {
std::cout << "Info: " << info << std::endl;
}
};
int main() {
ConcretePrototype original("Original Prototype");
original.printInfo();
// Clone the original prototype
Prototype* clone = original.clone(); // 使用克隆来复制一个新的对象,该对象与原对象相互独立
clone->printInfo();
delete clone;
return 0;
}
在这个示例中,Prototype 是原型基类,包含了两个纯虚函数:clone() 用于克隆对象,printInfo() 用于打印对象信息。ConcretePrototype 继承自 Prototype,并实现了克隆方法以及打印信息方法。在 main() 函数中,我们创建了一个原型对象 original,并通过克隆得到了一个新的对象 clone。
原型模式的主要优势在于:
- 减少对象的创建成本: 原型模式允许通过复制现有对象来创建新对象,避免了从头开始构建对象所需的初始化成本。这对于对象创建开销较大的情况下尤其有用,例如数据库连接、文件读写等。
- 提高性能: 在需要频繁创建相似对象的情况下,使用原型模式可以显著提高性能。因为克隆操作通常比对象的构造更加高效,不需要重新执行初始化工作。
- 简化对象创建过程: 当创建对象涉及复杂的步骤或多个参数时,使用原型模式可以避免繁琐的构造器调用,使对象的创建过程更加简单和清晰。
- 动态添加和删除原型: 原型模式允许在运行时动态地添加、删除和替换原型对象,从而增加了灵活性和扩展性。不需要修改现有代码,就可以引入新的原型对象。
- 隔离客户端和具体类: 客户端代码只需要与抽象的原型接口打交道,而不需要直接和具体的实现类交互。这样可以降低客户端代码与具体类的耦合度。
然而,也需要注意一些潜在的问题:
- 深拷贝的处理: 如果原型对象内部包含了其他引用类型的成员(如指针),需要确保正确实现深拷贝,以避免资源泄漏和不一致性。
- 内存消耗: 使用原型模式时,每个克隆的对象都会占用一定的内存空间。如果创建大量的克隆对象,可能会导致内存消耗过大。
- 原型的稳定性: 当原型对象的内部结构发生变化时,可能需要同时更新所有克隆对象的实现,否则可能导致克隆对象的行为不一致。
- 适用范围: 原型模式适用于那些需要创建相似对象且创建过程相对复杂的情况。如果对象的创建过程非常简单,可能没有必要使用原型模式。
综上所述,原型模式适用于需要频繁创建相似对象、降低创建成本、提高性能以及灵活扩展的场景,但在应用时需要根据具体情况权衡其优势和劣势。
结构型模式
1.适配器模式
用于将一个类的接口转换成另一个客户端所期望的接口,从而使原本不兼容的类能够协同工作。适配器模式常常用于整合已有的类库、系统或第三方组件,以便它们能够一起工作。
适配器模式有两种主要类型:类适配器和对象适配器。
- 类适配器: 使用单继承来适配一个类的接口到另一个接口。适配器类同时继承原有类并实现新接口。
- 对象适配器: 使用组合关系来将适配器与原有类组合,然后实现新接口。适配器类持有原有类的实例,并将调用转发给它。
#include <iostream>
// Target interface that the client expects
class Target {
public:
virtual void request() = 0;
};
// Adaptee: the class that needs to be adapted
class Adaptee {
public:
void specificRequest() {
std::cout << "Adaptee's specificRequest()" << std::endl;
}
};
// Adapter: adapts the Adaptee to the Target interface
class Adapter : public Target {
private:
Adaptee* adaptee;
public:
Adapter(Adaptee* adaptee) : adaptee(adaptee) {}
void request() override {
adaptee->specificRequest();
}
};
int main() {
Adaptee* adaptee = new Adaptee();
Target* adapter = new Adapter(adaptee);
adapter->request();
delete adaptee;
delete adapter;
return 0;
}
在这个示例中,Target 是客户端期望的接口,Adaptee 是需要被适配的类,Adapter 则是适配器类,将 Adaptee 的接口适配到 Target 接口上。在 main() 函数中,我们创建了一个 Adaptee 对象,并通过 Adapter 将其适配到 Target 接口,从而实现了适配器模式。
适配器模式的优势在于可以使不兼容的类协同工作,无需修改已有代码。然而,需要注意适配器模式可能引入一些间接性能开销,也需要仔细设计以确保适配器的正确性和稳定性。
适配器模式的主要好处在于能够在不修改现有代码的情况下,将不兼容的接口进行适配,从而实现多个组件、类或库之间的协同工作。以下是适配器模式的几个优点:
- 重用已有代码: 适配器模式允许你重用已有的类,即使它们的接口与你的系统不完全匹配。这可以节省开发时间和资源,避免从头开始重新实现功能。
- 无侵入性: 适配器模式不需要修改已有代码,因此不会影响已有功能的稳定性。这对于已经稳定运行的系统或第三方库非常有用,你只需实现适配器即可。
- 整合不同组件: 当你需要将多个不同来源的组件、类或库整合到一个系统中时,适配器模式可以用于统一这些不同的接口,使它们能够协同工作。
- 系统扩展性: 适配器模式使你能够轻松添加新的适配器类,从而实现与新的组件进行交互。这提高了系统的扩展性,允许你在不破坏现有结构的情况下引入新功能。
- 解耦性: 适配器模式可以降低不同组件之间的耦合度。客户端代码只需要与适配器接口交互,不需要关心具体的实现细节。
- 代码清晰性: 适配器模式可以使客户端代码更加清晰简洁,因为它屏蔽了底层组件的复杂性和差异性。
总之,适配器模式在整合不同系统、重用代码、维护系统稳定性和提高系统扩展性方面都非常有价值。它可以帮助你解决现有组件之间的接口不匹配问题,使系统更具灵活性和可维护性。
2.装饰器模式
它允许你在不改变现有对象结构的情况下,动态地向对象添加新的功能或责任。装饰器模式通过创建一系列装饰器类,每个装饰器类都包装了一个具体组件(被装饰的对象),从而实现了功能的逐层叠加。
装饰器模式的主要思想是将对象的功能划分为不同的层次,每个装饰器类只关注一种特定的功能,然后将这些装饰器类按照所需的顺序进行组合,以达到最终所需的功能组合。
#include <iostream>
#include <string>
class Coffee {
public:
virtual std::string getDescription() = 0;
virtual double cost() = 0;
};
class Espresso : public Coffee {
public:
std::string getDescription() override {
return "Espresso";
}
double cost() override {
return 1.99;
}
};
//装饰类
class Decorator : public Coffee {
protected:
Coffee* coffee;
public:
Decorator(Coffee* coffee) : coffee(coffee) {}
std::string getDescription() override {
return coffee->getDescription();
}
double cost() override {
return coffee->cost();
}
};
//继承装饰类后,增加自己的操作
class MilkDecorator : public Decorator {
public:
/*
Decorator(coffee) 是初始化列表中的一部分,它调用 Decorator 类的构造函数,
并将参数 coffee 传递给它。这是在 MilkDecorator 类中创建一个 Decorator 类的实例,
同时传递原始咖啡对象的指针给它
*/
MilkDecorator(Coffee* coffee) : Decorator(coffee) {}
std::string getDescription() override {
return coffee->getDescription() + ", Milk";
}
double cost() override {
return coffee->cost() + 0.5;
}
};
int main() {
Coffee* espresso = new Espresso();
std::cout << "Cost: " << espresso->cost() << ", Description: " << espresso->getDescription() << std::endl;
Coffee* espressoWithMilk = new MilkDecorator(new Espresso());
std::cout << "Cost: " << espressoWithMilk->cost() << ", Description: " << espressoWithMilk->getDescription() << std::endl;
delete espresso;
delete espressoWithMilk;
return 0;
}
在这个示例中,我们有一个基础类 Coffee,具体的咖啡类 Espresso 继承自它。然后,我们创建了一个名为 Decorator 的装饰器基类,它也继承自 Coffee,并包含了一个指向被装饰的对象的指针。MilkDecorator 是一个具体的装饰器类,它在咖啡上加入了牛奶。
在 main() 函数中,我们可以看到如何使用装饰器模式来创建不同种类的咖啡,然后逐层添加装饰器(如牛奶)来叠加功能。
在装饰器模式中,需要被装饰对象的指针是为了实现装饰器和被装饰对象之间的关联,从而可以逐层叠加功能。这种关联方式允许装饰器在不改变原有对象结构的情况下,为对象添加新的行为或责任。
装饰器模式的核心思想是通过创建一系列的装饰器类来包装具体组件(被装饰对象)。每个装饰器类都继承自共同的接口或基类,这样它们可以替代具体组件,然后逐层地添加新的功能。装饰器类内部持有一个指向被装饰对象的指针,通过这个指针,装饰器可以调用被装饰对象的方法,同时在其基础上添加额外的行为。
3.代理模式
它允许一个对象(代理)充当另一个对象(真实对象)的接口,以控制对这个对象的访问。代理对象可以起到过滤、保护、延迟加载等作用,从而对客户端提供更好的控制和支持。
代理模式分为多种类型,其中一些常见的包括:
- 远程代理(Remote Proxy): 用于在不同地址空间中控制对象的访问。远程代理在本地代理和远程对象之间充当中介,使得客户端可以通过本地代理调用远程对象的方法,而不需要直接与远程对象交互。
- 虚拟代理(Virtual Proxy): 当创建对象开销较大时,虚拟代理允许延迟对象的实际创建。虚拟代理在需要时才真正创建对象,以减少初始化和资源消耗。
- 保护代理(Protection Proxy): 用于控制对对象的访问权限。保护代理通过在访问前检查用户的权限,确保只有具有适当权限的用户可以访问对象。
- 缓存代理(Caching Proxy): 在调用方法之前,缓存代理会检查是否已经执行过相同的方法调用,如果有则直接返回缓存的结果,避免重复计算或访问。
- 智能代理(Smart Proxy): 智能代理在调用方法之前或之后执行附加的逻辑,例如记录方法调用日志、性能分析等。
#include <iostream>
// Subject interface
class Image {
public:
virtual void display() = 0;
};
// 实际类
class RealImage : public Image {
private:
std::string filename;
public:
RealImage(std::string filename) : filename(filename) {
loadFromDisk();
}
void display() override {
std::cout << "Displaying image: " << filename << std::endl;
}
void loadFromDisk() {
std::cout << "Loading image from disk: " << filename << std::endl;
}
};
// 代理类
class ProxyImage : public Image {
private:
RealImage* realImage;
std::string filename;
public:
ProxyImage(std::string filename) : filename(filename), realImage(nullptr) {}
void display() override {
if (realImage == nullptr) {
realImage = new RealImage(filename);
}
realImage->display();
}
};
int main() {
Image* image = new ProxyImage("sample.jpg");
// Image will be loaded and displayed when needed
image->display();//在第一次调用显示方法时,真正的对象才创建出来。
image->display();//第二次调用,就可以使用已经创造出的对象。
delete image;
return 0;
}
这段代码实现了代理模式的核心思想之一:延迟加载(Lazy Initialization)。让我来解释一下这段代码的作用和如何实现代理:
- 当客户端调用代理对象的 display() 方法时,首先会检查代理中的 realImage 成员是否为空(即是否已经创建了真实对象)。
- 如果 realImage 是空的,说明真实对象还没有被创建,于是代理对象会在这个时候创建一个新的 RealImage 对象,将图片文件名传递给它。
- 一旦 realImage 对象被创建,后续的调用就会直接使用这个已经存在的真实对象。
这段代码的作用是,在第一次需要显示图片的时候才真正创建并显示真实图片对象。在创建之前,代理对象充当了真实对象的接口,可以控制真实对象的创建时间,从而实现了延迟加载的效果。
这正是代理模式的一种用法:通过一个代理对象,控制对真实对象的访问,并在需要时才实际创建和使用真实对象。这样可以提升系统性能,减少不必要的资源开销,并在需要的时候才加载大量开销较大的对象。
4.外观模式
它提供了一个统一的接口,用于访问一组复杂子系统的功能。外观模式将复杂系统的内部结构与外部客户端代码隔离开来,使客户端只需要通过一个简单的接口与系统交互,而不需要了解系统的复杂实现细节。
外观模式的主要目标是简化客户端代码,同时降低客户端与子系统之间的耦合度。
#include <iostream>
// Subsystem classes
class CPU {
public:
void start() {
std::cout << "CPU is starting..." << std::endl;
}
};
class Memory {
public:
void load() {
std::cout << "Memory is loading..." << std::endl;
}
};
class HardDrive {
public:
void read() {
std::cout << "Hard Drive is reading..." << std::endl;
}
};
// Facade class
class ComputerFacade {
private:
CPU cpu;
Memory memory;
HardDrive hardDrive;
public:
void start() {
std::cout << "Computer is starting..." << std::endl;
cpu.start();
memory.load();
hardDrive.read();
std::cout << "Computer has started." << std::endl;
}
};
int main() {
ComputerFacade computer;
computer.start();
return 0;
}
- CPU、Memory 和 HardDrive 是子系统类,它们分别代表了计算机的不同部件。
- ComputerFacade 是外观类,提供了一个简化的接口来启动计算机。在 start() 方法中,通过调用子系统类的方法来完成计算机的启动过程。
在 main() 函数中,我们创建了一个 ComputerFacade 对象,并通过调用 start() 方法来启动计算机。客户端不需要知道子系统类的具体实现细节,只需要调用外观类的方法来完成任务。
外观模式的优势在于将复杂系统封装在一个简单的接口之后,提供了更高的抽象层次,使客户端代码更加清晰、简洁,同时降低了与子系统的耦合度。这在设计大型系统时特别有用,可以提供更好的可维护性和扩展性。
5.桥接模式
用于将抽象部分和实现部分分离,使它们可以独立地变化。桥接模式通过将多维度的变化分离开来,允许你在不修改现有代码的情况下,灵活地组合不同的抽象和实现。
这种模式适用于那些具有多个维度变化且可能相互影响的情况。通过桥接模式,你可以将每个维度的变化都抽象出来,然后通过组合不同的实现类和抽象类,来实现各种组合可能性。
#include <iostream>
// Implementor interface
class Renderer {
public:
virtual void renderCircle(float radius) = 0;
};
// Concrete Implementor 1
class VectorRenderer : public Renderer {
public:
void renderCircle(float radius) override {
std::cout << "Drawing a circle of radius " << radius << " using vector graphics." << std::endl;
}
};
// Concrete Implementor 2
class RasterRenderer : public Renderer {
public:
void renderCircle(float radius) override {
std::cout << "Drawing a circle of radius " << radius << " using raster graphics." << std::endl;
}
};
// Abstraction
class Shape {
protected:
Renderer* renderer;
public:
Shape(Renderer* renderer) : renderer(renderer) {}
virtual void draw() = 0;
virtual void resize(float factor) = 0;
};
// Refined Abstraction
class Circle : public Shape {
private:
float radius;
public:
Circle(Renderer* renderer, float radius) : Shape(renderer), radius(radius) {}
void draw() override {
renderer->renderCircle(radius);
}
void resize(float factor) override {
radius *= factor;
}
};
int main() {
Renderer* vectorRenderer = new VectorRenderer();
Renderer* rasterRenderer = new RasterRenderer();
Shape* circle1 = new Circle(vectorRenderer, 5);
circle1->draw();
circle1->resize(2);
circle1->draw();
Shape* circle2 = new Circle(rasterRenderer, 10);
circle2->draw();
circle2->resize(3);
circle2->draw();
delete vectorRenderer;
delete rasterRenderer;
delete circle1;
delete circle2;
return 0;
}
在这个示例中:
- Renderer 是实现者接口,定义了绘制图形的方法。
- VectorRenderer 和 RasterRenderer 是具体的实现者类,分别实现了 Renderer 接口,用于绘制不同类型的图形。
- Shape 是抽象类,它持有一个指向 Renderer 的指针,用于调用具体的绘制方法。
- Circle 是具体的扩展抽象类,它继承自 Shape,实现了绘制和调整尺寸的方法。
在 main() 函数中,我们创建了两个不同的实现者对象(VectorRenderer 和 RasterRenderer),以及两个不同的图形对象(Circle)。通过将不同的实现者对象传递给图形对象,可以灵活地实现不同类型的图形绘制。
桥接模式的优势在于将抽象和实现解耦,使系统更具灵活性和可扩展性。通过桥接模式,可以避免类爆炸问题,即大量的子类随着不同维度的变化而增加。这使得系统更容易维护和扩展。
6.组合模式
它允许你将对象组合成树状结构,以表示“部分-整体”的层次结构。组合模式使得客户端可以统一处理单个对象和组合对象,从而使代码更加通用和简洁。
在组合模式中,有两种主要类型的对象:
- 叶子(Leaf)对象: 代表树状结构中的叶节点,即不具有子节点的对象。
- 组合(Composite)对象: 代表树状结构中的分支节点,它可以包含多个子节点,可以是叶子对象,也可以是其他组合对象。
组合模式的核心思想是通过使用相同的接口,使客户端代码可以透明地处理单个对象和组合对象,从而简化代码并提高灵活性。
#include <iostream>
#include <vector>
// Component interface
class Component {
public:
virtual void operation() = 0;
};
// Leaf class
class Leaf : public Component {
public:
void operation() override {
std::cout << "Leaf operation." << std::endl;
}
};
// Composite class
class Composite : public Component {
private:
std::vector<Component*> children;
public:
void add(Component* component) {
children.push_back(component);
}
void operation() override {
std::cout << "Composite operation:" << std::endl;
for (Component* component : children) {
component->operation();
}
}
};
int main() {
Leaf* leaf1 = new Leaf();
Leaf* leaf2 = new Leaf();
Composite* composite = new Composite();
composite->add(leaf1);
composite->add(leaf2);
composite->operation();
delete leaf1;
delete leaf2;
delete composite;
return 0;
}
在这个示例中:
- Component 是组件接口,定义了组合对象和叶子对象的共同接口。
- Leaf 是叶子类,实现了 Component 接口,表示不具有子节点的对象。
- Composite 是组合类,实现了 Component 接口,表示具有子节点的对象。它可以包含多个子组件,无论是叶子对象还是其他组合对象。
在 main() 函数中,我们创建了一个组合对象 composite,并将两个叶子对象 leaf1 和 leaf2 添加到组合对象中。然后,调用组合对象的 operation() 方法,它会遍历并调用所有子组件的 operation() 方法。
组合模式的优势在于可以透明地处理单个对象和组合对象,使客户端代码更加通用和简洁。通过组合模式,可以构建出复杂的树状结构,并对整体和部分进行一致的处理。这对于处理层次结构的问题非常有用。
7.享元模式
它旨在通过共享对象的方式来最小化内存使用和提高性能。享元模式适用于需要大量相似对象的情况,通过共享相同的部分来减少内存消耗。
在享元模式中,对象分为两种:
- 内部状态(Intrinsic State): 这些状态存储在享元对象内部,它们通常是不会变化的。内部状态是可以共享的,因为不同的对象可以拥有相同的内部状态。
- 外部状态(Extrinsic State): 这些状态取决于特定的上下文,它们不会被共享,而是在需要时传递给享元对象。
//棋子颜色
enum PieceColor {BLACK, WHITE};
//棋子位置
struct PiecePos
{
int x;
int y;
PiecePos(int a, int b): x(a), y(b) {}
};
//棋子定义
class Piece
{
protected:
PieceColor m_color; //颜色
public:
Piece(PieceColor color): m_color(color) {}
~Piece() {}
virtual void Draw() {}
};
class BlackPiece: public Piece
{
public:
BlackPiece(PieceColor color): Piece(color) {}
~BlackPiece() {}
void Draw() { cout<<"绘制一颗黑棋\n"; }
};
class WhitePiece: public Piece
{
public:
WhitePiece(PieceColor color): Piece(color) {}
~WhitePiece() {}
void Draw() { cout<<"绘制一颗白棋\n";}
};
class PieceBoard
{
private:
vector<PiecePos> m_vecPos; //存放棋子的位置
Piece *m_blackPiece; //黑棋棋子
Piece *m_whitePiece; //白棋棋子
string m_blackName;
string m_whiteName;
public:
PieceBoard(string black, string white): m_blackName(black), m_whiteName(white)
{
m_blackPiece = NULL;
m_whitePiece = NULL;
}
~PieceBoard() { delete m_blackPiece; delete m_whitePiece;}
void SetPiece(PieceColor color, PiecePos pos)
{
if(color == BLACK)
{
if(m_blackPiece == NULL) //只有一颗黑棋
m_blackPiece = new BlackPiece(color);
cout<<m_blackName<<"在位置("<<pos.x<<','<<pos.y<<")";
m_blackPiece->Draw();
}
else
{
if(m_whitePiece == NULL)
m_whitePiece = new WhitePiece(color);
cout<<m_whiteName<<"在位置("<<pos.x<<','<<pos.y<<")";
m_whitePiece->Draw();
}
m_vecPos.push_back(pos);
}
};
假设有一个游戏中的棋盘,棋盘上有很多棋子,每个棋子都有一个颜色属性作为内部状态。如果使用享元模式,具有相同颜色的棋子可以共享同一个颜色对象,而不是为每个棋子都创建一个新的颜色对象。这样就可以减少内存使用,因为颜色是不变的,可以在多个棋子之间共享。
所以,“内部状态是共享的”意味着相同内部状态的对象可以共享一个实例,从而节省内存开销。
享元模式的优势在于可以节省内存开销,特别是当需要大量相似对象时。它通过共享内部状态来降低内存使用,同时保持了对象的不变性。这对于提高系统性能和减少内存消耗是非常有益的。
行为型模式
1.策略模式
它允许在运行时选择算法的一种方法。它的主要思想是将不同的算法封装成各自独立的类,使它们可以互相替换,而不影响客户端代码。
//首先,定义策略接口和具体策略类:
#include <iostream>
class DrawingStrategy {
public:
virtual void draw() = 0;
virtual ~DrawingStrategy() {}
};
class DrawCircle : public DrawingStrategy {
public:
void draw() override {
std::cout << "Drawing a circle." << std::endl;
}
};
class DrawRectangle : public DrawingStrategy {
public:
void draw() override {
std::cout << "Drawing a rectangle." << std::endl;
}
};
//然后,创建上下文类,它会持有一个策略对象并执行绘制操作
class DrawingContext {
private:
DrawingStrategy* strategy;
public:
DrawingContext(DrawingStrategy* strategy) : strategy(strategy) {}
void setStrategy(DrawingStrategy* newStrategy) {//这个的定义可以来换方法了。
strategy = newStrategy;
}
void executeDraw() {
strategy->draw();
}
};
//最后,在主函数中使用这些类来演示策略模式的工作方式
int main() {
DrawCircle drawCircle;
DrawRectangle drawRectangle;
DrawingContext context(&drawCircle);
context.executeDraw(); // 输出: Drawing a circle.
context.setStrategy(&drawRectangle);
context.executeDraw(); // 输出: Drawing a rectangle.
return 0;
}
在这个例子中,我们通过创建不同的绘制策略(DrawCircle和DrawRectangle)并将它们传递给上下文(DrawingContext)来实现策略模式。通过设置不同的策略,我们可以在运行时改变绘制的行为,而不需要修改上下文的代码。
2.模板方法模式
它定义了一个算法的框架,将算法中的某些步骤延迟到子类中实现。这样,算法的整体结构保持不变,但子类可以根据需要重新定义特定步骤的实现。模板方法模式主要用于在不同子类中保持算法结构的一致性,同时允许每个子类提供自己的实现细节。
模板方法模式的核心思想是:定义一个抽象基类,其中包含一个模板方法,该方法定义了算法的框架,包括一系列的步骤,有些步骤可以在基类中实现,而有些步骤需要在子类中实现。
//定义模板方法抽象基类:
class CookingRecipe {
public:
void cook() {
selectIngredients();
prepareIngredients();
cookDish();
serveDish();
}
virtual void selectIngredients() {
std::cout << "Selecting generic ingredients." << std::endl;
}
virtual void prepareIngredients() {
std::cout << "Preparing generic ingredients." << std::endl;
}
virtual void cookDish() = 0;
virtual void serveDish() {
std::cout << "Serving the dish." << std::endl;
}
virtual ~CookingRecipe() {}
};
//创建具体子类,实现特定的炒菜步骤:
class SpaghettiRecipe : public CookingRecipe {
public:
void selectIngredients() override {
std::cout << "Selecting spaghetti ingredients." << std::endl;
}
void prepareIngredients() override {
std::cout << "Preparing spaghetti ingredients." << std::endl;
}
void cookDish() override {
std::cout << "Cooking spaghetti." << std::endl;
}
void serveDish() override {
std::cout << "Serving spaghetti." << std::endl;
}
};
class StirFryRecipe : public CookingRecipe {
public:
void selectIngredients() override {
std::cout << "Selecting stir-fry ingredients." << std::endl;
}
void cookDish() override {
std::cout << "Stir-frying the ingredients." << std::endl;
}
};
//在主函数中使用这些类:
int main() {
CookingRecipe* spaghetti = new SpaghettiRecipe();
spaghetti->cook();
CookingRecipe* stirFry = new StirFryRecipe();
stirFry->cook();
delete spaghetti;
delete stirFry;
return 0;
}
在这个例子中,CookingRecipe 类定义了炒菜的模板方法框架,其中包含了选材料、切菜、炒菜等步骤,其中一些步骤在基类中已经有了通用的实现。子类 SpaghettiRecipe 和 StirFryRecipe 分别实现了特定菜式的具体步骤。通过调用 cook 方法,可以按照模板方法定义的步骤来完成炒菜过程。
3.观察者模式
它用于在对象之间定义一种依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都能够自动收到通知并进行相应的更新。观察者模式将一个主题(或被观察者)和多个观察者对象分离,使得它们可以独立变化,同时又能保持一定的协调性。
//定义观察者接口和主题接口:
#include <vector>
#include <iostream>
class Observer {
public:
virtual void update(float temperature, float humidity, float pressure) = 0;
virtual ~Observer() {}
};
class Subject {
public:
virtual void registerObserver(Observer* observer) = 0;
virtual void removeObserver(Observer* observer) = 0;
virtual void notifyObservers() = 0;
virtual ~Subject() {}
};
//创建具体的观察者和主题类:
class WeatherStation : public Subject {
private:
std::vector<Observer*> observers;
float temperature;
float humidity;
float pressure;
public:
void registerObserver(Observer* observer) override {
observers.push_back(observer);
}
void removeObserver(Observer* observer) override {
// Remove observer from the vector
}
void notifyObservers() override {
for (Observer* observer : observers) {
observer->update(temperature, humidity, pressure);
}
}
void setMeasurements(float temp, float hum, float press) {
temperature = temp;
humidity = hum;
pressure = press;
measurementsChanged();
}
void measurementsChanged() {
notifyObservers();
}
};
class Display : public Observer {
public:
virtual void display() = 0;
};
class CurrentConditionsDisplay : public Display {
private:
float temperature;
float humidity;
public:
void update(float temp, float hum, float press) override {
temperature = temp;
humidity = hum;
display();
}//这个是观察者模式的核心,可以更新。
void display() override {
std::cout << "Current conditions: " << temperature << "F degrees and " << humidity << "% humidity." << std::endl;
}
};
//在主函数中使用这些类:
int main() {
WeatherStation weatherStation;
CurrentConditionsDisplay display1;
weatherStation.registerObserver(&display1);
// Simulate weather changes
weatherStation.setMeasurements(80, 65, 30.4);
weatherStation.setMeasurements(82, 70, 29.2);
return 0;
}
在这个例子中,WeatherStation 是主题,它维护了一组观察者,当气象信息发生变化时,会通知所有观察者进行更新。CurrentConditionsDisplay 是一个具体的观察者,它实现了 Observer 接口,当收到更新通知时,它会显示当前的气象信息。通过使用观察者模式,我们实现了主题和观察者之间的解耦,使得它们可以独立变化。
4.迭代子模式
它提供了一种顺序访问聚合对象(如列表、集合、数组等)中各个元素的方法,而不暴露聚合对象的内部结构。这种模式可以让你在不需要了解聚合对象的内部结构的情况下,遍历其中的元素。
//定义迭代子接口和聚合接口:
class Iterator {
public:
virtual bool hasNext() = 0;
virtual std::string next() = 0;
virtual ~Iterator() {}
};
class Aggregate {
public:
virtual Iterator* createIterator() = 0;
virtual ~Aggregate() {}
};
//创建具体迭代子和具体聚合类:
class BookIterator : public Iterator {
private:
std::vector<std::string> books;
size_t position;
public:
BookIterator(const std::vector<std::string>& bookList) : books(bookList), position(0) {}
bool hasNext() override {
return position < books.size();
}
std::string next() override {
if (hasNext()) {
return books[position++];
}
return "";
}
};
class BookShelf : public Aggregate {
private:
std::vector<std::string> books;
public:
void addBook(const std::string& book) {
books.push_back(book);
}
Iterator* createIterator() override {
return new BookIterator(books);
}
};
//在主函数中使用这些类:
int main() {
BookShelf bookShelf;
bookShelf.addBook("Book 1");
bookShelf.addBook("Book 2");
bookShelf.addBook("Book 3");
Iterator* iterator = bookShelf.createIterator();
while (iterator->hasNext()) {
std::cout << iterator->next() << std::endl;
}
delete iterator;
return 0;
}
在这个例子中,BookIterator 实现了迭代子接口,它通过一个 vector 来保存书籍列表,并在遍历时逐个返回书籍。BookShelf 实现了聚合接口,它负责创建一个迭代子对象,供客户端使用。
通过使用迭代子模式,我们可以遍历聚合对象的元素,而不必直接操作其内部结构,这有助于将遍历与聚合对象的实现解耦。
5.责任链模式
它用于将多个处理对象组成一条链,每个对象都可以处理特定类型的请求,并将未处理的请求传递给链中的下一个对象。这种模式可以避免将发送者和接收者之间的耦合,同时提供了一种灵活的方式来处理请求。
//定义处理请求的基类和具体处理者类:
#include <iostream>
#include <string>
class Approver {
protected:
Approver* successor;
public:
void setSuccessor(Approver* next) {
successor = next;
}
virtual void processRequest(const std::string& request) = 0;
virtual ~Approver() {}
};
class ConcreteApprover : public Approver {
private:
std::string name;
int approvalLimit;
public:
ConcreteApprover(const std::string& n, int limit) : name(n), approvalLimit(limit) {}
void processRequest(const std::string& request) override {
if (approvalLimit >= request.length()) {
std::cout << name << " approved the request: " << request << std::endl;
} else if (successor) {
successor->processRequest(request);
}
}
};
//在主函数中使用这些类:
int main() {
Approver* manager = new ConcreteApprover("Manager", 1000);
Approver* director = new ConcreteApprover("Director", 5000);
Approver* vp = new ConcreteApprover("Vice President", 10000);
manager->setSuccessor(director);//设置manager的下一个领导
director->setSuccessor(vp);//设置direct的下一个领导
manager->processRequest("Buy a pen.");
manager->processRequest("Buy a laptop.");
manager->processRequest("Buy a new office building.");
delete manager;
delete director;
delete vp;
return 0;
}
在这个例子中,Approver 是处理请求的基类,ConcreteApprover 是具体的处理者类,每个处理者有一个审批限制,超过限制则传递请求给下一个处理者。通过设置处理者的链,请求会按照顺序传递,直到找到合适的处理者处理请求,或者请求没有被处理。
责任链模式允许你动态地构建一个处理请求的链,并且可以在运行时改变链中的顺序或新增处理者,这为处理请求提供了更大的灵活性。
6.命令模式
它将请求(命令)封装成一个对象,从而使得请求的发送者和接收者解耦,并且可以在运行时灵活地组合不同的请求。这种模式可以用于实现撤销、重做、队列等功能。
//定义命令接口和具体命令类:
class Command {
public:
virtual void execute() = 0;
virtual ~Command() {}
};
class TV {
public:
void turnOn() {
std::cout << "TV is turned on." << std::endl;
}
void turnOff() {
std::cout << "TV is turned off." << std::endl;
}
};
class TVOnCommand : public Command {
private:
TV* tv;
public:
TVOnCommand(TV* t) : tv(t) {}
void execute() override {
tv->turnOn();
}
};
class TVOffCommand : public Command {
private:
TV* tv;
public:
TVOffCommand(TV* t) : tv(t) {}
void execute() override {
tv->turnOff();
}
};
//创建遥控器类和客户端:
class RemoteControl {
private:
Command* onButton;
Command* offButton;
public:
RemoteControl(Command* on, Command* off) : onButton(on), offButton(off) {}
void pressOnButton() {
onButton->execute();
}
void pressOffButton() {
offButton->execute();
}
};
//在主函数中使用这些类:
int main() {
TV tv;
TVOnCommand tvOn(&tv);
TVOffCommand tvOff(&tv);
RemoteControl remote(&tvOn, &tvOff);
remote.pressOnButton(); // 打开电视
remote.pressOffButton(); // 关闭电视
return 0;
}
在这个例子中,Command 是命令接口,TVOnCommand 和 TVOffCommand 是具体的命令类,它们分别封装了打开电视和关闭电视的操作。RemoteControl 是遥控器类,它持有不同的命令对象,并可以通过调用命令对象的 execute 方法来执行相应的操作。
通过使用命令模式,我们将请求封装成了对象,实现了请求的发送者和接收者的解耦,同时也为实现撤销、重做等功能提供了一种方便的方式。
7.备忘录模式
它允许你在不破坏封装性的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态,以便在需要时恢复对象到先前的状态。这种模式通常用于实现撤销、恢复等功能。
//定义备忘录类、原始对象类和管理类:
#include <string>
class Memento {//存储数据的位置
private:
std::string state;
public:
Memento(const std::string& s) : state(s) {}
std::string getState() const {
return state;
}
};
class TextEditor {//c操作数据的位置
private:
std::string text;
public:
void setText(const std::string& t) {
text = t;
}
std::string getText() const {
return text;
}
Memento createMemento() const {
return Memento(text);
}
void restoreMemento(const Memento& m) {
text = m.getState();
}
};
class Caretaker {//存储数据的方法
private:
Memento memento;
public:
void saveState(const TextEditor& editor) {
memento = editor.createMemento();
}
void restoreState(TextEditor& editor) {
editor.restoreMemento(memento);
}
};
//在主函数中使用这些类:
int main() {
TextEditor editor;
Caretaker caretaker;
editor.setText("Hello, World!");
caretaker.saveState(editor);
editor.setText("This is a new text.");
std::cout << "Current text: " << editor.getText() << std::endl;
caretaker.restoreState(editor);
std::cout << "Restored text: " << editor.getText() << std::endl;
return 0;
}
在这个例子中,Memento 是备忘录类,它用于保存对象的状态。TextEditor 是原始对象类,用户可以输入文本,创建备忘录,以及恢复到之前的状态。Caretaker 是管理类,负责保存和恢复状态。
通过使用备忘录模式,我们可以在不破坏封装性的情况下,实现对象的状态保存和恢复。这对于需要实现撤销、恢复等功能非常有用。
8.状态模式
它允许一个对象在内部状态改变时改变其行为,看起来好像对象的类发生了改变。状态模式将对象的状态封装成不同的状态类,对象在不同的状态下可以表现出不同的行为,从而使得状态转换更加可控。
//定义状态接口和具体状态类:
class AudioPlayer;
class State {
public:
virtual void play() = 0;
virtual void pause() = 0;
virtual void stop() = 0;
virtual ~State() {}
};
class PlayingState : public State {
private:
AudioPlayer* player;
public:
PlayingState(AudioPlayer* p) : player(p) {}
void play() override {
std::cout << "Already playing." << std::endl;
}
void pause() override;
void stop() override {
std::cout << "Stopped playing." << std::endl;
player->changeState(nullptr);
}
};
class PausedState : public State {
private:
AudioPlayer* player;
public:
PausedState(AudioPlayer* p) : player(p) {}
void play() override;
void pause() override {
std::cout << "Already paused." << std::endl;
}
void stop() override {
std::cout << "Stopped playing." << std::endl;
player->changeState(nullptr);
}
};
class StoppedState : public State {
private:
AudioPlayer* player;
public:
StoppedState(AudioPlayer* p) : player(p) {}
void play() override;
void pause() override {
std::cout << "Can't pause, already stopped." << std::endl;
}
void stop() override {
std::cout << "Already stopped." << std::endl;
}
};
//创建音频播放器类:
class AudioPlayer {
private:
State* currentState;
public:
AudioPlayer() : currentState(nullptr) {}
void changeState(State* newState) {
delete currentState;
currentState = newState;
}
void play() {
if (currentState) {
currentState->play();
}
}
void pause() {
if (currentState) {
currentState->pause();
}
}
void stop() {
if (currentState) {
currentState->stop();
}
}
};
//在主函数中使用这些类:
int main() {
AudioPlayer player;
player.play(); // 无效
player.pause(); // 无效
player.stop(); // 无效
player.changeState(new PlayingState(&player));
player.play(); // 播放中
player.pause(); // 暂停中
player.stop(); // 停止中
player.changeState(new PausedState(&player));
player.play(); // 播放中
player.pause(); // 已暂停
player.stop(); // 停止中
player.changeState(new StoppedState(&player));
player.play(); // 播放中
player.pause(); // 无法暂停
player.stop(); // 已停止
return 0;
}
在这个例子中,State 是状态接口,PlayingState、PausedState 和 StoppedState 是具体状态类,每个状态类实现了不同状态下的操作。AudioPlayer 是音频播放器类,它持有当前状态对象,并通过状态对象调用不同的操作。
通过使用状态模式,我们可以在不同的状态下执行不同的操作,使得状态的转换更加可控。这种模式在需要根据对象的状态改变行为的场景中非常有用。
9.访问者模式
它允许你在不改变被访问类的结构的前提下,定义一个新的操作(访问者),用于对该类中的元素进行操作。访问者模式将数据结构和操作分离,使得新增操作时不需要修改已有的数据结构。
//定义动物基类和访问者接口:
class Animal;
class Visitor {
public:
virtual void visitLion(Animal* lion) = 0;
virtual void visitTiger(Animal* tiger) = 0;
virtual void visitBear(Animal* bear) = 0;
virtual ~Visitor() {}
};
class Animal {
public:
virtual void accept(Visitor* visitor) = 0;
virtual ~Animal() {}
};
//创建具体的动物类和具体的访问者类:
class Lion : public Animal {
public:
void accept(Visitor* visitor) override {
visitor->visitLion(this);
}
};
class Tiger : public Animal {
public:
void accept(Visitor* visitor) override {
visitor->visitTiger(this);
}
};
class Bear : public Animal {
public:
void accept(Visitor* visitor) override {
visitor->visitBear(this);
}
};
class Veterinarian : public Visitor {
public:
void visitLion(Animal* lion) override {
std::cout << "Veterinarian is checking the lion's health." << std::endl;
}
void visitTiger(Animal* tiger) override {
std::cout << "Veterinarian is checking the tiger's health." << std::endl;
}
void visitBear(Animal* bear) override {
std::cout << "Veterinarian is checking the bear's health." << std::endl;
}
};
class Feeder : public Visitor {
public:
void visitLion(Animal* lion) override {
std::cout << "Feeder is giving food to the lion." << std::endl;
}
void visitTiger(Animal* tiger) override {
std::cout << "Feeder is giving food to the tiger." << std::endl;
}
void visitBear(Animal* bear) override {
std::cout << "Feeder is giving food to the bear." << std::endl;
}
};
//在主函数中使用这些类:
int main() {
Lion lion;
Tiger tiger;
Bear bear;
Veterinarian veterinarian;
Feeder feeder;
lion.accept(&veterinarian);
lion.accept(&feeder);
tiger.accept(&veterinarian);
tiger.accept(&feeder);
bear.accept(&veterinarian);
bear.accept(&feeder);
return 0;
}
在这个例子中,Visitor 是访问者接口,Veterinarian 和 Feeder 是具体的访问者类,它们分别实现了不同的访问操作。Animal 是动物基类,Lion、Tiger 和 Bear 是具体的动物类,它们实现了 accept 方法,将自身作为参数传递给访问者。
通过使用访问者模式,我们可以在不改变动物类结构的情况下,定义并添加新的访问操作。这种模式对于需要对一个复杂的对象结构进行多种操作的场景非常有用。
10.中介者模式
它用于降低多个对象之间的直接耦合,通过引入一个中介者对象,来协调对象之间的交互。中介者模式通过集中控制对象之间的通信,减少了对象之间的相互依赖,使得系统更加可维护和可扩展。
//定义中介者接口和具体中介者类:
#include <iostream>
#include <string>
#include <vector>
class User;
class ChatMediator {
public:
virtual void sendMessage(const std::string& message, User* user) = 0;
virtual ~ChatMediator() {}
};
class ChatRoom : public ChatMediator {
private:
std::vector<User*> users;
public:
void addUser(User* user) {
users.push_back(user);
}
void sendMessage(const std::string& message, User* sender) override {
for (User* user : users) {
if (user != sender) {
user->receiveMessage(message);
}
}
}
};
//创建用户类:
class User {
protected:
ChatMediator* mediator;
std::string name;
public:
User(ChatMediator* m, const std::string& n) : mediator(m), name(n) {}
virtual void send(const std::string& message) = 0;
virtual void receiveMessage(const std::string& message) = 0;
};
class ChatUser : public User {
public:
ChatUser(ChatMediator* m, const std::string& n) : User(m, n) {}
void send(const std::string& message) override {
std::cout << name << " sends: " << message << std::endl;
mediator->sendMessage(message, this);
}
void receiveMessage(const std::string& message) override {
std::cout << name << " receives: " << message << std::endl;
}
};
//在主函数中使用这些类:
int main() {
ChatMediator* chatRoom = new ChatRoom();
User* user1 = new ChatUser(chatRoom, "User 1");
User* user2 = new ChatUser(chatRoom, "User 2");
User* user3 = new ChatUser(chatRoom, "User 3");
chatRoom->addUser(user1);
chatRoom->addUser(user2);
chatRoom->addUser(user3);
user1->send("Hello, everyone!");
user2->send("Hi there!");
delete user1;
delete user2;
delete user3;
delete chatRoom;
return 0;
}
在这个例子中,ChatMediator 是中介者接口,ChatRoom 是具体的中介者类,它负责协调用户之间的通信。User 是用户基类,ChatUser 是具体的用户类,它们通过中介者来发送和接收消息。
通过使用中介者模式,我们将对象之间的通信集中到中介者中,从而降低了对象之间的耦合,使得系统更加灵活和可扩展。这种模式特别适用于需要多个对象协调合作的场景。
11.解释器模式
它用于定义一种语言的文法规则,并且提供一种解释器来解释这种语言中的表达式。该模式可以用于构建解释器、编译器等应用,它将语言解释和实现解耦,使得可以更加灵活地处理不同的语法规则。
//定义表达式接口和具体表达式类:
class Expression {
public:
virtual int interpret() = 0;
virtual ~Expression() {}
};
class NumberExpression : public Expression {
private:
int number;
public:
NumberExpression(int n) : number(n) {}
int interpret() override {
return number;
}
};
class AdditionExpression : public Expression {
private:
Expression* left;
Expression* right;
public:
AdditionExpression(Expression* l, Expression* r) : left(l), right(r) {}
int interpret() override {
return left->interpret() + right->interpret();
}
~AdditionExpression() {
delete left;
delete right;
}
};
//在主函数中使用这些类:
int main() {
Expression* expression = new AdditionExpression(
new NumberExpression(5),
new AdditionExpression(
new NumberExpression(10),
new NumberExpression(2)
)
);
int result = expression->interpret();
std::cout << "Result: " << result << std::endl;
delete expression;
return 0;
}
在这个例子中,Expression 是表达式接口,NumberExpression 和 AdditionExpression 是具体的表达式类,分别用于表示数字和加法表达式。AdditionExpression 类接收两个表达式作为操作数,并在 interpret 方法中实现加法操作。
通过使用解释器模式,我们可以实现一个简单的表达式解释器,用于计算不同形式的表达式。这种模式在需要构建解释器或编译器等应用中非常有用。