目录
设计模式概述
六大设计原则
从整体理解六大设计原则
单例模式
饿汉模式:
懒汉模式:
线程安全的懒汉模式
工厂模式
简单工厂模式
抽象工厂模式
建造者模式(Builder Pattern)
代理模式(Proxy Pattern)
总结
设计模式概述
设计模式是前辈们总结出的开发经验,是一系列用来解决特定问题的套路。它不是语法规范,而是帮助我们提升代码复用性、可维护性、可读性、稳健性以及安全性的一套方案。
六大设计原则
-
单一职责原则(Single Responsibility Principle)
-
定义:类的职责应该单一,一个方法只做一件事。这样能使得每次改动的范围最小化。
-
使用建议:不同的功能应分开到不同的类中。
-
例子:网络聊天中,网络通信和聊天应该分为网络通信类和聊天类。
-
📌 总结:每个类应该专注于完成一个功能。
-
-
开闭原则(Open Closed Principle)
-
定义:对扩展开放,对修改封闭。即程序应该允许扩展,但不应该修改已有的代码。
-
使用建议:通过扩展已有代码来实现新功能,而不是修改原有功能。
-
例子:在超时卖货中,通过新增促销价格而不是修改原有商品价格。
-
⚙️ 总结:改动最小化,扩展能力最强。
-
-
里氏替换原则(Liskov Substitution Principle)
-
定义:子类应该能够替换父类并且不改变程序的正确性。
-
使用建议:子类必须完全实现父类的方法,并确保方法的输入参数可被扩展,输出可以缩小。
-
例子:跑步运动员类 - 子类可以是长跑运动员或短跑运动员,但是都能执行父类的跑步方法。
-
👣 总结:继承体系不能破坏,子类需完全实现父类的行为。
-
-
依赖倒置原则(Dependence Inversion Principle)
-
定义:高层模块不应该依赖低层模块,两者都应该依赖抽象(接口)。
-
使用建议:模块间通过接口依赖,避免直接依赖具体类。
-
例子:司机类依赖于抽象接口,不依赖于具体车型。
-
🚗 总结:依赖接口而非具体类,保证灵活性和扩展性。
-
-
迪米特法则(Law of Demeter)
-
定义:一个对象应该对它的朋友有最少的了解,不应该依赖于朋友的朋友。
-
使用建议:减少类之间的耦合,限制类之间的交互。
-
例子:老师点名时,老师只交给班长一个名单,班长负责点名,而不是班长直接点名。
-
🔒 总结:降低类间耦合,减少不必要的依赖。
-
-
接口隔离原则(Interface Segregation Principle)
-
定义:客户端不应依赖它不需要的接口。
-
使用建议:接口设计应该精简,避免暴露无用的方法。
-
例子:修改密码的接口不应包含修改其他用户信息的功能。
-
🔑 总结:接口简洁,每个接口只应提供相关功能。
-
从整体理解六大设计原则
-
单一职责原则:实现类要职责单一。
-
里氏替换原则:保持继承体系的完整性。
-
依赖倒置原则:依赖接口编程,避免具体实现依赖。
-
接口隔离原则:设计接口时要精简。
-
迪米特法则:降低类之间的耦合。
-
开闭原则:扩展系统时应对扩展开放,对修改封闭。
单例模式
单例模式确保一个类只有一个实例,并提供全局访问点。适用于需要全局共享数据或配置的场景。
这种方式的优势在于简单且线程安全,但它的缺点是程序启动时就创建了实例,可能会浪费一些内存资源,特别是如果程序从未使用这个单例对象的话。
饿汉模式:
-
程序启动时就创建单一实例,适用于多线程环境,性能高。
🐯 饿汉模式实现:
#include <iostream> //1.饿汉模式 //--不管用不用,先实例化一个出来再说class singleton {//--构造析构私有化,防止外部创建对象 private:singleton() {};~singleton() {}; public:--拷贝构造赋值重载delete,防止拷贝和赋值singleton(const singleton& s) = delete;singleton& operator=(const singleton& s) = delete;// 静态成员函数,用于获取单例实例static singleton& getInstance() {return instance;}//静态成员函数,单例的方法static void Work() { std::cout << "我是单例的方法" << std::endl; }; private://静态成员变量static singleton instance; };// 初始化静态成员变量 singleton singleton::instance;int main() {// 获取单例实例singleton& sg = singleton::getInstance();sg.Work();return 0; }
-
总结
-
饿汉式单例模式:在程序启动时就创建单例实例,无论是否使用它。这样可以避免多线程问题,确保实例的创建是线程安全的。
-
私有化构造函数和析构函数:确保外部不能直接创建或销毁实例,保护了单例的唯一性。
-
删除拷贝构造和赋值运算符:避免单例对象被拷贝或赋值,保证了全局只有一个实例。
-
懒汉模式:
-
第一次使用时才创建实例,适用于资源消耗较大时。
⏳ 懒汉模式实现:
class singleton {//--构造析构私有化,防止外部创建对象 private:singleton() {};~singleton() {}; public:--拷贝构造赋值重载delete,防止拷贝和赋值singleton(const singleton& s) = delete;singleton& operator=(const singleton& s) = delete;// 静态成员函数,用于获取单例实例,在使用时调用才创建实例static singleton& getInstance() {//创建实例static singleton instance;return instance;}//静态成员函数,单例的方法static void Work() { std::cout << "我是单例的方法" << std::endl; }; };int main() {// 获取单例实例singleton& sg = singleton::getInstance();sg.Work();return 0; }
总结
-
懒汉式单例模式:与饿汉式不同,懒汉式单例在第一次需要使用时才会创建实例,避免了资源的浪费。
-
私有化构造函数和析构函数:保证类的实例无法在外部被创建或销毁。
-
删除拷贝构造和赋值运算符:避免了单例对象的复制和赋值,确保了全局只有一个实例。
-
静态局部变量:通过在
getInstance
中使用static
局部变量,我们实现了懒汉式单例模式,在第一次调用时实例化,之后保持唯一。
懒汉式单例模式的优点:
-
实例只会在第一次使用时创建,节省了资源。
-
避免了程序启动时就创建实例的开销。
缺点:
-
如果单例的创建过程很耗时,可能会导致首次调用时的延迟。
-
在多线程环境下,虽然C++11确保了线程安全,但如果不小心修改代码,仍可能出现线程安全问题。
这种方式适用于实例创建开销较大,且程序中不会在一开始就需要该单例实例的场景。
线程安全的懒汉模式
使用 std::mutex
来保证线程安全
通过使用互斥锁(std::mutex
),我们可以确保在多线程环境下,只有一个线程能够创建单例实例。其基本思路是每次访问getInstance
时,使用锁来保护实例的创建过程。
#include <iostream>
#include <mutex> // 引入互斥锁库class singleton {
private:singleton() {}; // 构造函数私有化~singleton() {}; // 析构函数私有化public:// 禁止拷贝和赋值singleton(const singleton& s) = delete;singleton& operator=(const singleton& s) = delete;// 静态成员函数,用于获取单例实例static singleton& getInstance() {std::lock_guard<std::mutex> lock(mtx); // 加锁,保证线程安全if (!instance) { // 如果实例还没有创建instance = new singleton();}return *instance;}// 单例的方法static void Work() { std::cout << "我是单例的方法" << std::endl; }private:static singleton* instance; // 单例实例指针static std::mutex mtx; // 互斥锁
};// 初始化静态成员变量
singleton* singleton::instance = nullptr;
std::mutex singleton::mtx;int main() {// 获取单例实例singleton& sg = singleton::getInstance();sg.Work();return 0;
}
解释:
-
std::mutex mtx;
:我们添加了一个静态的std::mutex
,用于控制对单例实例创建的访问。 -
std::lock_guard<std::mutex> lock(mtx);
:使用lock_guard
来自动加锁和解锁,保证线程在访问实例时不会发生竞争条件。 -
if (!instance)
:只有当instance
为空时,我们才会创建单例实例。
这种方法通过互斥锁确保了多线程环境下的线程安全,但它可能会导致性能开销,特别是在锁的竞争激烈时。
工厂模式
工厂模式是一种创建型设计模式,提供了创建对象的最佳方式。在工厂模式中,我们通过工厂类来创建对象,避免暴露创建对象的细节。
简单工厂模式
一个工厂类根据传入的参数来决定创建哪个类的实例。缺点是扩展性差。
-
🏭 简单工厂实现:
class Fruit { public:virtual void show() = 0; }; class Apple : public Fruit { public:void show() { std::cout << "我是苹果" << std::endl; } }; class Banana : public Fruit { public:void show() { std::cout << "我是香蕉" << std::endl; } }; class FruitFactory { public:static std::shared_ptr<Fruit> create(const std::string &name) {if (name == "苹果") return std::make_shared<Apple>();else if (name == "香蕉") return std::make_shared<Banana>();return nullptr;} };
简单工厂模式通过一个工厂类来决定实例化哪个具体的产品类。它将对象的创建集中到工厂类中,客户端不需要直接创建对象,只需要通过工厂来获取。
优点:
客户端解耦:客户端代码不需要知道具体产品类的创建细节,只通过工厂来获取对象,降低了客户端与产品类的耦合度。
集中管理:所有产品的创建集中管理,便于修改和维护。
易于扩展:增加新的产品只需要在工厂中加入相应的逻辑,不需要改动其他类的代码。
缺点:
违反开闭原则:每次增加新的产品类型时,都需要修改工厂类的代码,违反了开闭原则(对扩展开放,对修改关闭)。
难以扩展产品种类:如果产品种类过多,工厂类将变得臃肿,管理变得复杂。
不够灵活:如果有多个工厂需要不同的产品,简单工厂模式不能很好地支持这种情况。
-
🍏 工厂方法模式实现:
#include <iostream> #include <string> #include <memory>// ------------------------------------ // 抽象产品类:水果类 // ------------------------------------ class Fruit { public:Fruit() {} // 构造函数virtual void show() = 0; // 纯虚函数,要求每个子类实现该方法 };// ------------------------------------ // 具体产品类:苹果类 // ------------------------------------ class Apple : public Fruit { public:Apple() {} // 构造函数virtual void show() {std::cout << "我是一个苹果" << std::endl; // 展示水果的名字} private:std::string _color; // 苹果的颜色 };// ------------------------------------ // 具体产品类:香蕉类 // ------------------------------------ class Banana : public Fruit { public:Banana() {} // 构造函数virtual void show() {std::cout << "我是一个香蕉" << std::endl; // 展示水果的名字} };// ------------------------------------ // 抽象工厂类:水果工厂类 // ------------------------------------ class FruitFactory { public:virtual std::shared_ptr<Fruit> create() = 0; // 纯虚函数,具体工厂类需要实现此方法来创建相应的产品 };// ------------------------------------ // 具体工厂类:苹果工厂类 // ------------------------------------ class AppleFactory : public FruitFactory { public:virtual std::shared_ptr<Fruit> create() {return std::make_shared<Apple>(); // 创建并返回一个苹果对象} };// ------------------------------------ // 具体工厂类:香蕉工厂类 // ------------------------------------ class BananaFactory : public FruitFactory { public:virtual std::shared_ptr<Fruit> create() {return std::make_shared<Banana>(); // 创建并返回一个香蕉对象} };// ------------------------------------ // 主函数:演示如何使用工厂方法模式 // ------------------------------------ int main() {// 创建苹果工厂对象std::shared_ptr<FruitFactory> factory(new AppleFactory()); // 通过苹果工厂创建苹果产品对象std::shared_ptr<Fruit> fruit = factory->create(); fruit->show(); // 调用苹果的show方法,输出:我是一个苹果// 重置工厂为香蕉工厂对象factory.reset(new BananaFactory()); // 通过香蕉工厂创建香蕉产品对象fruit = factory->create(); fruit->show(); // 调用香蕉的show方法,输出:我是一个香蕉return 0; }
工厂方法模式的优缺点:
优点:
减轻了工厂类的负担:通过将不同类型的产品的生产交给不同的具体工厂类,可以避免一个工厂类处理太多不同类型产品的复杂逻辑。
符合开闭原则:当需要添加新产品时,只需要创建新的工厂类并实现
create()
方法,而不需要修改原有的工厂类和产品类。缺点:
类的数量增加:每增加一种新的产品类型,就需要创建一个新的工厂类,这可能导致类的数量激增,管理和维护变得困难。
需要更多的代码:每个产品的工厂都需要有一个独立的类,可能会增加代码的复杂度。
抽象工厂模式
进一步抽象出工厂结构,通过抽象工厂来创建不同系列的对象。适用于产品族的管理。
-
🌐 抽象工厂模式实现:
#include <iostream> #include <string> #include <memory>// 抽象⼯⼚模式:围绕⼀个超级⼯⼚创建其他⼯⼚。每个⽣成的⼯⼚按照⼯⼚模式提供对象。 // 思想:将⼯⼚抽象成两层,抽象⼯⼚ & 具体⼯⼚⼦类, 在⼯⼚⼦类中⽣产不同类型的⼦产品// 抽象水果类,定义了水果的公共接口 class Fruit { public:// 构造函数Fruit() {}// 纯虚函数,用于展示水果信息,具体实现由子类完成virtual void show() = 0; };// 苹果类,继承自水果类,实现了具体的水果信息展示 class Apple : public Fruit { public:// 构造函数Apple() {}// 实现抽象基类的纯虚函数,输出苹果信息virtual void show() {std::cout << "我是⼀个苹果" << std::endl;} private:// 苹果的颜色属性std::string _color; };// 香蕉类,继承自水果类,实现了具体的水果信息展示 class Banana : public Fruit { public:// 构造函数Banana() {}// 实现抽象基类的纯虚函数,输出香蕉信息virtual void show() {std::cout << "我是⼀个⾹蕉" << std::endl;} };// 抽象动物类,定义了动物的公共接口 class Animal { public:// 纯虚函数,用于输出动物的声音,具体实现由子类完成virtual void voice() = 0; };// 小羊类,继承自动物类,实现了具体的动物声音输出 class Lamp : public Animal { public:// 实现抽象基类的纯虚函数,输出小羊的声音void voice() { std::cout << "咩咩咩\n"; } };// 小狗类,继承自动物类,实现了具体的动物声音输出 class Dog : public Animal { public:// 实现抽象基类的纯虚函数,输出小狗的声音void voice() { std::cout << "汪汪汪\n"; } };// 抽象工厂类,定义了创建水果和动物对象的接口 class Factory { public:// 纯虚函数,用于根据名称创建水果对象virtual std::shared_ptr<Fruit> getFruit(const std::string &name) = 0;// 纯虚函数,用于根据名称创建动物对象virtual std::shared_ptr<Animal> getAnimal(const std::string &name) = 0; };// 水果工厂类,继承自抽象工厂类,专门用于创建水果对象 class FruitFactory : public Factory { public:// 实现抽象工厂类的接口,由于该工厂只负责创建水果,所以返回空指针virtual std::shared_ptr<Animal> getAnimal(const std::string &name) {return std::shared_ptr<Animal>();}// 实现抽象工厂类的接口,根据名称创建具体的水果对象virtual std::shared_ptr<Fruit> getFruit(const std::string &name) {if (name == "苹果") {return std::make_shared<Apple>();} else if (name == "⾹蕉") {return std::make_shared<Banana>();}// 如果名称不匹配,返回空指针return std::shared_ptr<Fruit>();} };// 动物工厂类,继承自抽象工厂类,专门用于创建动物对象 class AnimalFactory : public Factory { public:// 实现抽象工厂类的接口,由于该工厂只负责创建动物,所以返回空指针virtual std::shared_ptr<Fruit> getFruit(const std::string &name) {return std::shared_ptr<Fruit>();}// 实现抽象工厂类的接口,根据名称创建具体的动物对象virtual std::shared_ptr<Animal> getAnimal(const std::string &name) {if (name == "⼩⽺") {return std::make_shared<Lamp>();} else if (name == "⼩狗") {return std::make_shared<Dog>();}// 如果名称不匹配,返回空指针return std::shared_ptr<Animal>();} };// 工厂生产者类,用于根据名称创建具体的工厂对象 class FactoryProducer { public:// 静态方法,根据名称返回具体的工厂对象static std::shared_ptr<Factory> getFactory(const std::string &name) {if (name == "动物") {return std::make_shared<AnimalFactory>();} else {return std::make_shared<FruitFactory>();}} };int main() {// 通过工厂生产者获取水果工厂对象std::shared_ptr<Factory> fruit_factory = FactoryProducer::getFactory("水果");// 通过水果工厂创建苹果对象std::shared_ptr<Fruit> fruit = fruit_factory->getFruit("苹果");// 调用苹果对象的展示方法fruit->show();// 通过水果工厂创建香蕉对象fruit = fruit_factory->getFruit("⾹蕉");// 调用香蕉对象的展示方法fruit->show();// 通过工厂生产者获取动物工厂对象std::shared_ptr<Factory> animal_factory = FactoryProducer::getFactory("动物");// 通过动物工厂创建小羊对象std::shared_ptr<Animal> animal = animal_factory->getAnimal("⼩⽺");// 调用小羊对象的声音输出方法animal->voice();// 通过动物工厂创建小狗对象animal = animal_factory->getAnimal("⼩狗");// 调用小狗对象的声音输出方法animal->voice();return 0; }
抽象工厂模式通过多个具体工厂类来创建相关联的产品家族。它为每一类产品提供一个独立的工厂,客户端通过工厂接口来获取产品,而不关心具体是哪种产品。
优点:
符合开闭原则:通过增加新的具体工厂类,客户端代码无需修改,符合开闭原则。只需要增加新的工厂类,而不需要修改已有代码。
支持产品族的组合:如果产品之间存在某种关联或需要组合使用(例如,一个UI界面上的按钮和输入框需要配合使用),抽象工厂模式能够很方便地提供相关产品。
灵活性高:可以创建一组相关的产品,可以在不同的产品之间切换,适应不同的产品需求。
缺点:
复杂性较高:由于涉及多个工厂类和产品类,代码结构变得更加复杂,理解和使用上也相对较为繁琐。
难以扩展新的产品族:增加新的产品族时,不仅需要增加新产品,还需要在多个工厂类中修改代码,导致扩展不够灵活。
无法为单一产品创建实例:抽象工厂模式强制要求工厂创建一组相关产品,不能像简单工厂那样只创建单个产品实例。
建造者模式(Builder Pattern)
建造者模式是一种创建型设计模式,它允许你将复杂对象的构建过程与表示分离,从而使得同样的构建过程可以创建不同的表示。换句话说,建造者模式通过逐步构建和装配来创建一个复杂对象,可以让创建过程更加灵活且易于管理。
主要目的
建造者模式的主要目的是解决对象构建过程过于复杂的问题。通常,一个复杂对象由多个部分组成,且这些部分的初始化或组合顺序对对象的最终状态至关重要。建造者模式将复杂对象的构建过程分解为多个步骤,每一步完成不同的任务,而最终的产品则通过指挥者类(Director)来完成。
核心组成部分
建造者模式主要包括以下四个核心类:
-
抽象产品类(Product)
代表一个复杂的对象,它由多个部件组成,这些部件由构建者类(Builder)逐步创建并组装起来。 -
具体产品类(Concrete Product)
实现了抽象产品类的具体实现,它将多个部件组合成一个完整的产品。 -
抽象建造者类(Builder)
定义了创建产品各个部件的接口。它规定了构建产品的步骤,并提供了对每个部件的构建接口。 -
具体建造者类(Concrete Builder)
继承自抽象建造者类,具体实现了各个部件的构建方法。每个建造者负责将产品的不同部分组合成完整的产品。 -
指挥者类(Director)
负责统一产品的构建过程。它通过调用建造者的方法,逐步组装产品,并最终返回构建好的产品。
代码示例
以下是一个简单的建造者模式的实现示例,模拟了一个构建“电脑”的过程:
#include <iostream>
#include <string>// 抽象产品类
class Computer {
public:virtual void show() = 0;
};// 具体产品类:表示电脑
class PC : public Computer {
public:void show() override {std::cout << "这是一个个人电脑" << std::endl;}
};class Laptop : public Computer {
public:void show() override {std::cout << "这是一个笔记本电脑" << std::endl;}
};// 抽象建造者类
class ComputerBuilder {
public:virtual void buildCPU() = 0;virtual void buildRAM() = 0;virtual void buildStorage() = 0;virtual Computer* getResult() = 0;
};// 具体建造者类:PC建造者
class PCBuilder : public ComputerBuilder {
private:PC* pc;
public:PCBuilder() {pc = new PC();}void buildCPU() override {std::cout << "为PC组装CPU" << std::endl;}void buildRAM() override {std::cout << "为PC组装内存" << std::endl;}void buildStorage() override {std::cout << "为PC组装硬盘" << std::endl;}Computer* getResult() override {return pc;}
};// 具体建造者类:Laptop建造者
class LaptopBuilder : public ComputerBuilder {
private:Laptop* laptop;
public:LaptopBuilder() {laptop = new Laptop();}void buildCPU() override {std::cout << "为Laptop组装CPU" << std::endl;}void buildRAM() override {std::cout << "为Laptop组装内存" << std::endl;}void buildStorage() override {std::cout << "为Laptop组装硬盘" << std::endl;}Computer* getResult() override {return laptop;}
};// 指挥者类
class Director {
private:ComputerBuilder* builder;
public:Director(ComputerBuilder* b) : builder(b) {}// 指挥者负责产品的建造过程void construct() {builder->buildCPU();builder->buildRAM();builder->buildStorage();}Computer* getComputer() {return builder->getResult();}
};// 客户端代码
int main() {// 创建PC产品PCBuilder* pcBuilder = new PCBuilder();Director director(pcBuilder);director.construct();Computer* pc = director.getComputer();pc->show();// 创建Laptop产品LaptopBuilder* laptopBuilder = new LaptopBuilder();director = Director(laptopBuilder);director.construct();Computer* laptop = director.getComputer();laptop->show();delete pcBuilder;delete laptopBuilder;delete pc;delete laptop;return 0;
}
解释:
抽象产品类
Computer
定义了产品的接口(例如show
)。具体产品类
PC
和Laptop
分别实现了Computer
类,代表不同的产品类型。抽象建造者类
ComputerBuilder
定义了产品构建的步骤接口,如buildCPU
、buildRAM
等。具体建造者类
PCBuilder
和LaptopBuilder
实现了具体的建造过程,分别用于创建不同类型的电脑。指挥者类
Director
负责指挥具体建造者来构建产品。它通过construct()
方法来控制构建过程。
适用场景
当一个对象的构建过程复杂,但希望通过不同的步骤实现构建时。
当创建的对象可以有多个不同的表示形式时。
当构建过程需要独立于对象的表示形式时。
优点:
清晰的分工:各个建造者类负责具体的产品部件构建,符合单一职责原则。
灵活性:可以在不改变构建过程的情况下,创建不同的产品。
易于扩展:只需扩展新的具体建造者类即可创建新的产品,不需要修改已有代码。
缺点:
类数量增多:需要定义多个建造者类,可能会使得代码结构相对复杂。
复杂性:对于较简单的对象,使用建造者模式可能显得过于复杂。
代理模式(Proxy Pattern)
代理模式是一种结构型设计模式,其核心思想是通过一个代理对象来控制对原对象的访问。也就是说,代理对象在客户端和目标对象之间充当中介的角色,允许我们在不直接访问目标对象的情况下,对其进行操作。
主要目标
代理模式的目的是解决在某些情况下,某个对象不适合或不能直接被引用访问时,通过代理对象来进行中介访问。代理模式为目标对象的操作提供了灵活的控制,增强了程序的扩展性和可维护性。
代理模式的组成结构:
-
目标对象(RealSubject)
目标对象是代理模式中真正的业务逻辑实现者,它是代理所要控制的对象。代理对象最终会将请求转发给目标对象。 -
代理对象(Proxy)
代理对象负责控制对目标对象的访问,可以进行一些额外的操作,如权限控制、延迟加载、日志记录等。代理对象通常与目标对象实现相同的接口。 -
客户端(Client)
客户端通过代理对象与目标对象进行交互,客户端不直接访问目标对象。
示例:租房中的代理模式
假设有一个房东需要将房子出租,但房东本身不能直接完成所有的出租任务(如带看房、发布租房广告等),因此他决定委托给中介来完成这些工作。这里,房东是目标对象,而中介是代理对象。
代码实现
#include <iostream>
#include <string>// 目标对象:房东(RealSubject)
class Landlord {
public:void rentHouse() {std::cout << "房东:出租房子" << std::endl;}void repairHouse() {std::cout << "房东:修理房子" << std::endl;}
};// 代理对象:中介(Proxy)
class Agent {
private:Landlord* landlord;
public:Agent(Landlord* l) : landlord(l) {}// 代理方法:发布招租广告void advertise() {std::cout << "中介:发布房子招租广告" << std::endl;}// 代理方法:带客户看房void showHouse() {std::cout << "中介:带客户看房" << std::endl;}// 代理方法:代理房东出租房子void rentHouse() {advertise(); // 先发布广告showHouse(); // 带客户看房landlord->rentHouse(); // 最后房东出租房子}// 代理方法:代理房东修理房子void repairHouse() {landlord->repairHouse();}
};// 客户端代码
int main() {Landlord* landlord = new Landlord(); // 房东对象Agent* agent = new Agent(landlord); // 代理对象(中介)// 客户端通过中介来租房和修理房子agent->rentHouse();agent->repairHouse();delete landlord;delete agent;return 0;
}
解释:
目标对象(Landlord):代表房东,负责房子的出租和维修。
代理对象(Agent):代表中介,负责发布广告、带看房子以及转发房东的出租和维修请求。
客户端:客户端通过代理对象(中介)来租房和修理房子,代理对象控制了对房东(目标对象)的访问。
代理模式的优缺点:
优点:
控制访问:代理可以控制对目标对象的访问,例如权限控制、延迟加载等。
灵活性:通过代理类可以实现透明的代理和动态代理,使得可以在不修改目标类的前提下实现功能扩展。
增强功能:代理模式可以在不改变目标类的基础上增加额外的功能,如日志记录、缓存等。
缺点:
增加了复杂性:引入代理类会使得系统的结构变得更加复杂。
性能问题:在某些情况下,代理模式会导致额外的调用层级,从而影响程序性能。
总结
设计模式提供了一种优化代码的方式,通过标准化、模块化的设计,能够帮助我们提高系统的可维护性、可扩展性和可复用性。掌握这些设计模式和原则,将有助于开发高质量的代码,并且在实际开发中轻松应对复杂问题。
🎯 掌握设计模式 = 更加优雅、灵活的编程!