设计模式 | 详解常用设计模式(六大设计原则,单例模式,工厂模式,建造者模式,代理模式)

目录

设计模式概述

六大设计原则

从整体理解六大设计原则

单例模式

饿汉模式:

懒汉模式:

线程安全的懒汉模式

工厂模式

简单工厂模式

抽象工厂模式

[建造者模式(Builder Pattern)](#建造者模式(Builder Pattern))

[代理模式(Proxy Pattern)](#代理模式(Proxy Pattern))

总结


设计模式概述

设计模式是前辈们总结出的开发经验,是一系列用来解决特定问题的套路 。它不是语法规范,而是帮助我们提升代码复用性、可维护性、可读性、稳健性 以及安全性的一套方案。


六大设计原则

  1. 单一职责原则(Single Responsibility Principle)

    • 定义:类的职责应该单一,一个方法只做一件事。这样能使得每次改动的范围最小化。

    • 使用建议:不同的功能应分开到不同的类中。

    • 例子网络聊天 中,网络通信和聊天应该分为网络通信类聊天类

    • 📌 总结:每个类应该专注于完成一个功能。

  2. 开闭原则(Open Closed Principle)

    • 定义:对扩展开放,对修改封闭。即程序应该允许扩展,但不应该修改已有的代码。

    • 使用建议:通过扩展已有代码来实现新功能,而不是修改原有功能。

    • 例子 :在超时卖货中,通过新增促销价格而不是修改原有商品价格。

    • ⚙️ 总结:改动最小化,扩展能力最强。

  3. 里氏替换原则(Liskov Substitution Principle)

    • 定义:子类应该能够替换父类并且不改变程序的正确性。

    • 使用建议:子类必须完全实现父类的方法,并确保方法的输入参数可被扩展,输出可以缩小。

    • 例子跑步运动员类 - 子类可以是长跑运动员短跑运动员,但是都能执行父类的跑步方法。

    • 👣 总结:继承体系不能破坏,子类需完全实现父类的行为。

  4. 依赖倒置原则(Dependence Inversion Principle)

    • 定义:高层模块不应该依赖低层模块,两者都应该依赖抽象(接口)。

    • 使用建议:模块间通过接口依赖,避免直接依赖具体类。

    • 例子司机类依赖于抽象接口,不依赖于具体车型。

    • 🚗 总结:依赖接口而非具体类,保证灵活性和扩展性。

  5. 迪米特法则(Law of Demeter)

    • 定义:一个对象应该对它的朋友有最少的了解,不应该依赖于朋友的朋友。

    • 使用建议:减少类之间的耦合,限制类之间的交互。

    • 例子老师点名时,老师只交给班长一个名单,班长负责点名,而不是班长直接点名。

    • 🔒 总结:降低类间耦合,减少不必要的依赖。

  6. 接口隔离原则(Interface Segregation Principle)

    • 定义:客户端不应依赖它不需要的接口。

    • 使用建议:接口设计应该精简,避免暴露无用的方法。

    • 例子:修改密码的接口不应包含修改其他用户信息的功能。

    • 🔑 总结:接口简洁,每个接口只应提供相关功能。


从整体理解六大设计原则

  • 单一职责原则:实现类要职责单一。

  • 里氏替换原则:保持继承体系的完整性。

  • 依赖倒置原则:依赖接口编程,避免具体实现依赖。

  • 接口隔离原则:设计接口时要精简。

  • 迪米特法则:降低类之间的耦合。

  • 开闭原则:扩展系统时应对扩展开放,对修改封闭。


单例模式

单例模式确保一个类只有一个实例,并提供全局访问点。适用于需要全局共享数据或配置的场景。

这种方式的优势在于简单且线程安全,但它的缺点是程序启动时就创建了实例,可能会浪费一些内存资源,特别是如果程序从未使用这个单例对象的话。

饿汉模式
  • 程序启动时就创建单一实例,适用于多线程环境,性能高。

    🐯 饿汉模式实现

    cpp 复制代码
    #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;
    }
  • 总结

  • 饿汉式单例模式:在程序启动时就创建单例实例,无论是否使用它。这样可以避免多线程问题,确保实例的创建是线程安全的。

  • 私有化构造函数和析构函数:确保外部不能直接创建或销毁实例,保护了单例的唯一性。

  • 删除拷贝构造和赋值运算符:避免单例对象被拷贝或赋值,保证了全局只有一个实例。

懒汉模式
  • 第一次使用时才创建实例,适用于资源消耗较大时。

    懒汉模式实现

    cpp 复制代码
    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;
    }

总结

  1. 懒汉式单例模式 :与饿汉式不同,懒汉式单例在第一次需要使用时才会创建实例,避免了资源的浪费。

  2. 私有化构造函数和析构函数 :保证类的实例无法在外部被创建或销毁。

  3. 删除拷贝构造和赋值运算符 :避免了单例对象的复制和赋值,确保了全局只有一个实例。

  4. 静态局部变量 :通过在getInstance中使用static局部变量,我们实现了懒汉式单例模式,在第一次调用时实例化,之后保持唯一。

懒汉式单例模式的优点

  • 实例只会在第一次使用时创建,节省了资源。

  • 避免了程序启动时就创建实例的开销。

缺点

  • 如果单例的创建过程很耗时,可能会导致首次调用时的延迟。

  • 在多线程环境下,虽然C++11确保了线程安全,但如果不小心修改代码,仍可能出现线程安全问题。

这种方式适用于实例创建开销较大,且程序中不会在一开始就需要该单例实例的场景。

线程安全的懒汉模式

使用 std::mutex 来保证线程安全

通过使用互斥锁(std::mutex),我们可以确保在多线程环境下,只有一个线程能够创建单例实例。其基本思路是每次访问getInstance时,使用锁来保护实例的创建过程。

cpp 复制代码
#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为空时,我们才会创建单例实例。

这种方法通过互斥锁确保了多线程环境下的线程安全,但它可能会导致性能开销,特别是在锁的竞争激烈时。


工厂模式

工厂模式是一种创建型设计模式,提供了创建对象的最佳方式。在工厂模式中,我们通过工厂类来创建对象,避免暴露创建对象的细节。

简单工厂模式

一个工厂类根据传入的参数来决定创建哪个类的实例。缺点是扩展性差。

  • 🏭 简单工厂实现

    cpp 复制代码
    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;
        }
    };

简单工厂模式通过一个工厂类来决定实例化哪个具体的产品类。它将对象的创建集中到工厂类中,客户端不需要直接创建对象,只需要通过工厂来获取。

优点:

  • 客户端解耦:客户端代码不需要知道具体产品类的创建细节,只通过工厂来获取对象,降低了客户端与产品类的耦合度。

  • 集中管理:所有产品的创建集中管理,便于修改和维护。

  • 易于扩展:增加新的产品只需要在工厂中加入相应的逻辑,不需要改动其他类的代码。

缺点:

  • 违反开闭原则:每次增加新的产品类型时,都需要修改工厂类的代码,违反了开闭原则(对扩展开放,对修改关闭)。

  • 难以扩展产品种类:如果产品种类过多,工厂类将变得臃肿,管理变得复杂。

  • 不够灵活:如果有多个工厂需要不同的产品,简单工厂模式不能很好地支持这种情况。

  • 🍏 工厂方法模式实现

    cpp 复制代码
    #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;  
    }

工厂方法模式的优缺点:

优点:

  1. 减轻了工厂类的负担:通过将不同类型的产品的生产交给不同的具体工厂类,可以避免一个工厂类处理太多不同类型产品的复杂逻辑。

  2. 符合开闭原则 :当需要添加新产品时,只需要创建新的工厂类并实现create()方法,而不需要修改原有的工厂类和产品类。

缺点:

  1. 类的数量增加:每增加一种新的产品类型,就需要创建一个新的工厂类,这可能导致类的数量激增,管理和维护变得困难。

  2. 需要更多的代码:每个产品的工厂都需要有一个独立的类,可能会增加代码的复杂度。

抽象工厂模式

进一步抽象出工厂结构,通过抽象工厂来创建不同系列的对象。适用于产品族的管理。

  • 🌐 抽象工厂模式实现

    cpp 复制代码
    #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)来完成。

核心组成部分

建造者模式主要包括以下四个核心类:

  1. 抽象产品类(Product)

    代表一个复杂的对象,它由多个部件组成,这些部件由构建者类(Builder)逐步创建并组装起来。

  2. 具体产品类(Concrete Product)

    实现了抽象产品类的具体实现,它将多个部件组合成一个完整的产品。

  3. 抽象建造者类(Builder)

    定义了创建产品各个部件的接口。它规定了构建产品的步骤,并提供了对每个部件的构建接口。

  4. 具体建造者类(Concrete Builder)

    继承自抽象建造者类,具体实现了各个部件的构建方法。每个建造者负责将产品的不同部分组合成完整的产品。

  5. 指挥者类(Director)

    负责统一产品的构建过程。它通过调用建造者的方法,逐步组装产品,并最终返回构建好的产品。

代码示例

以下是一个简单的建造者模式的实现示例,模拟了一个构建"电脑"的过程:

cpp 复制代码
#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;
}

解释:

  1. 抽象产品类 Computer定义了产品的接口(例如show)。

  2. 具体产品类 PCLaptop分别实现了Computer类,代表不同的产品类型。

  3. 抽象建造者类 ComputerBuilder定义了产品构建的步骤接口,如buildCPUbuildRAM等。

  4. 具体建造者类 PCBuilderLaptopBuilder实现了具体的建造过程,分别用于创建不同类型的电脑。

  5. 指挥者类 Director负责指挥具体建造者来构建产品。它通过construct()方法来控制构建过程。

适用场景

  • 当一个对象的构建过程复杂,但希望通过不同的步骤实现构建时。

  • 当创建的对象可以有多个不同的表示形式时。

  • 当构建过程需要独立于对象的表示形式时。

优点:

  • 清晰的分工:各个建造者类负责具体的产品部件构建,符合单一职责原则。

  • 灵活性:可以在不改变构建过程的情况下,创建不同的产品。

  • 易于扩展:只需扩展新的具体建造者类即可创建新的产品,不需要修改已有代码。

缺点:

  • 类数量增多:需要定义多个建造者类,可能会使得代码结构相对复杂。

  • 复杂性:对于较简单的对象,使用建造者模式可能显得过于复杂。

代理模式(Proxy Pattern)

代理模式是一种结构型设计模式,其核心思想是通过一个代理对象来控制对原对象的访问。也就是说,代理对象在客户端和目标对象之间充当中介的角色,允许我们在不直接访问目标对象的情况下,对其进行操作。

主要目标

代理模式的目的是解决在某些情况下,某个对象不适合或不能直接被引用访问时,通过代理对象来进行中介访问。代理模式为目标对象的操作提供了灵活的控制,增强了程序的扩展性和可维护性。

代理模式的组成结构:

  1. 目标对象(RealSubject)

    目标对象是代理模式中真正的业务逻辑实现者,它是代理所要控制的对象。代理对象最终会将请求转发给目标对象。

  2. 代理对象(Proxy)

    代理对象负责控制对目标对象的访问,可以进行一些额外的操作,如权限控制、延迟加载、日志记录等。代理对象通常与目标对象实现相同的接口。

  3. 客户端(Client)

    客户端通过代理对象与目标对象进行交互,客户端不直接访问目标对象。

示例:租房中的代理模式

假设有一个房东需要将房子出租,但房东本身不能直接完成所有的出租任务(如带看房、发布租房广告等),因此他决定委托给中介来完成这些工作。这里,房东是目标对象,而中介是代理对象。

代码实现

cpp 复制代码
#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):代表中介,负责发布广告、带看房子以及转发房东的出租和维修请求。

  • 客户端:客户端通过代理对象(中介)来租房和修理房子,代理对象控制了对房东(目标对象)的访问。

代理模式的优缺点:

优点:

  • 控制访问:代理可以控制对目标对象的访问,例如权限控制、延迟加载等。

  • 灵活性:通过代理类可以实现透明的代理和动态代理,使得可以在不修改目标类的前提下实现功能扩展。

  • 增强功能:代理模式可以在不改变目标类的基础上增加额外的功能,如日志记录、缓存等。

缺点:

  • 增加了复杂性:引入代理类会使得系统的结构变得更加复杂。

  • 性能问题:在某些情况下,代理模式会导致额外的调用层级,从而影响程序性能。


总结

设计模式提供了一种优化代码的方式,通过标准化、模块化的设计,能够帮助我们提高系统的可维护性、可扩展性和可复用性。掌握这些设计模式和原则,将有助于开发高质量的代码,并且在实际开发中轻松应对复杂问题。

🎯 掌握设计模式 = 更加优雅、灵活的编程!

相关推荐
RanceGru32 分钟前
C++——调用OpenCV和NVIDIA Video Codec SDK库实现使用GPU硬解码MP4视频文件
c++·opencv·算法·gpu算力·视频编解码
点云SLAM1 小时前
C++ 中自主内存管理 new/delete 与 malloc/free 完全详解
c++·算法·指针·内存管理·new/delete·malloc/free·内存地址
爱凤的小光2 小时前
图漾官网Sample_V1版本C++语言完整参考例子---单相机版本
开发语言·c++·数码相机
青瓦梦滋2 小时前
【语法】C++的继承
开发语言·c++
ttk2192 小时前
【算法练习】归并排序和归并分治
数据结构·c++·算法·排序算法
程序员JerrySUN3 小时前
设计模式每日硬核训练 Day 17:中介者模式(Mediator Pattern)完整讲解与实战应用
microsoft·设计模式·中介者模式
梁辰兴3 小时前
数据结构:实验7.3Huffman树与Huffman编码
数据结构·c++·算法·c
小_t_同学3 小时前
C++之类和对象:构造函数,析构函数,拷贝构造,赋值运算符重载
开发语言·c++
wuqingshun3141593 小时前
经典算法 最长单调递增子序列
java·c++·算法·蓝桥杯·机器人