代理模式(Proxy Pattern)详解

代理模式(Proxy Pattern)详解

一、什么是代理模式?

代理模式是一种结构型设计模式,通过创建代理对象来间接访问目标对象(委托类),实现对目标对象的访问控制、权限验证、延迟加载或功能增强。代理对象起到中介作用,它不仅仅是对目标对象的直接替代,还能在访问目标对象时增加一些额外的逻辑。


二、代理模式的应用场景
  1. 权限控制:只有特定角色才能访问某些资源或功能。
  2. 远程访问:通过代理访问分布式系统中的远程对象(如RMI)。
  3. 延迟加载(虚拟代理):只有在需要时才创建并初始化资源密集型对象。
  4. 记录日志:在方法调用时记录日志信息。
  5. 缓存代理:对目标对象的返回值进行缓存,减少对目标对象的直接访问。
  6. 智能引用:当访问对象时,执行额外操作,如引用计数或对象销毁检查。

三、现实中的例子
  1. 公司助理(代理人)
    • 助理根据客户身份控制是否允许客户见到老板。
    • 助理可以代替老板处理简单事务,减少老板的工作量。
  2. 视频网站权限控制
    • 免费用户只能观看免费内容。
    • VIP用户可以观看更多高级内容。
    • 某些高级内容需VIP加额外券才能观看。

四、代理模式的组成
  1. 抽象类或接口
    定义代理类和目标类的公共方法。代理类和目标类都实现该接口,保证代理和目标对象可以互换。

    class VideoSet {
    public:
        virtual void freeMovie() = 0;
        virtual void vipMovie() = 0;
        virtual void ticketMovie() = 0;
        virtual ~VideoSet() = default;
    };
    
  2. 目标类(委托类)
    实现了具体业务逻辑,是实际处理请求的类。

    class FixBugVideoSet : public VideoSet {
    public:
        void freeMovie() override {
            std::cout << "观看免费电影" << std::endl;
        }
        void vipMovie() override {
            std::cout << "观看VIP电影" << std::endl;
        }
        void ticketMovie() override {
            std::cout << "用券观看电影" << std::endl;
        }
    };
    
  3. 代理类
    代理类通过持有目标类的引用或指针间接访问目标类。代理类可以在调用目标类方法前后添加额外逻辑。

    class ProxyVideoSet : public VideoSet {
    private:
        VideoSet* realVideoSet;  // 持有目标对象的指针
    public:
        ProxyVideoSet(VideoSet* videoSet) : realVideoSet(videoSet) {}
        void freeMovie() override {
            realVideoSet->freeMovie();  // 直接调用目标对象方法
        }
        void vipMovie() override {
            std::cout << "您目前是普通游客,需要升级为VIP才能观看VIP电影" << std::endl;
        }
        void ticketMovie() override {
            std::cout << "您没有电影券,需要购买券才能观看此电影" << std::endl;
        }
    };
    
  4. 客户端
    客户端通过代理类访问目标对象。客户端只知道代理类的存在,不需要直接操作目标对象。

    int main() {
        FixBugVideoSet realVideoSet;
        ProxyVideoSet proxy(&realVideoSet);
    
        proxy.freeMovie();  // 免费电影正常观看
        proxy.vipMovie();   // 提示需要VIP权限
        proxy.ticketMovie(); // 提示需要购买券
        return 0;
    }
    

五、代理模式的分类
  1. 静态代理

    • 代理类在编译时由开发者定义,逻辑固定。
    • 实现简单,但扩展性较差。
  2. 动态代理

    • 在运行时动态生成代理对象。
    • 动态代理通过拦截方法调用并添加逻辑,适合场景复杂的情况(如Java的InvocationHandler)。
    • C++中可使用装饰器模式与动态代理结合实现类似效果。
  3. 保护代理

    • 控制对象的访问权限。
    • 例如视频网站的用户分级功能,普通用户只能访问免费内容。
  4. 虚拟代理

    • 延迟加载目标对象。
    • 例如在图片加载场景中,只有图片被实际访问时才初始化。
  5. 远程代理

    • 本地代理代替客户端访问远程对象。
    • 常见于分布式系统中的RMI或gRPC。
  6. 缓存代理

    • 对目标对象的返回值进行缓存。
    • 例如数据库查询代理,减少重复查询。

六、代理模式的优缺点
  1. 优点

    • 访问控制:实现灵活的权限验证。
    • 职责分离:目标对象专注于核心功能,代理对象负责额外操作。
    • 增强功能:可在不修改目标类的情况下添加新功能,如日志、缓存等。
    • 提高可扩展性:通过引入代理,方便后续修改和扩展。
  2. 缺点

    • 系统复杂性增加:引入代理类可能使代码结构更加复杂。
    • 性能开销:代理对象增加了一层间接调用,可能影响系统性能。

七、代理模式与其他设计模式的区别
  • 装饰器模式
    • 代理模式侧重于控制访问,而装饰器模式是为了增强功能
    • 装饰器模式通常在不改变对象接口的情况下,动态为对象添加行为。
  • 适配器模式
    • 适配器模式用于改变接口以兼容旧代码,代理模式则是为了控制访问和权限

八、代理模式的完整案例
#include 
#include 

class VideoSet {
public:
    virtual void freeMovie() = 0;
    virtual void vipMovie() = 0;
    virtual void ticketMovie() = 0;
    virtual ~VideoSet() = default;
};

class FixBugVideoSet : public VideoSet {
public:
    void freeMovie() override {
        std::cout << "观看免费电影" << std::endl;
    }
    void vipMovie() override {
        std::cout << "观看VIP电影" << std::endl;
    }
    void ticketMovie() override {
        std::cout << "用券观看电影" << std::endl;
    }
};

class ProxyVideoSet : public VideoSet {
private:
    std::unique_ptr<VideoSet> realVideoSet;
    bool isVIP;
    bool hasTicket;
public:
    ProxyVideoSet(bool vip, bool ticket)
        : realVideoSet(std::make_unique<FixBugVideoSet>()), isVIP(vip), hasTicket(ticket) {}

    void freeMovie() override {
        realVideoSet->freeMovie();
    }

    void vipMovie() override {
        if (isVIP) {
            realVideoSet->vipMovie();
        } else {
            std::cout << "您目前是普通游客,需要升级为VIP才能观看VIP电影" << std::endl;
        }
    }

    void ticketMovie() override {
        if (isVIP && hasTicket) {
            realVideoSet->ticketMovie();
        } else if (!isVIP) {
            std::cout << "您需要升级为VIP才能购买电影券" << std::endl;
        } else {
            std::cout << "您没有电影券,请先购买" << std::endl;
        }
    }
};

int main() {
    ProxyVideoSet visitor(false, false);  // 普通用户
    ProxyVideoSet vipUser(true, true);   // VIP用户

    visitor.freeMovie();  // 观看免费电影
    visitor.vipMovie();   // 提示需要升级为VIP
    visitor.ticketMovie();// 提示需要购买电影券

    vipUser.freeMovie();  // VIP用户观看免费电影
    vipUser.vipMovie();   // VIP用户观看VIP电影
    vipUser.ticketMovie();// VIP用户观看用券电影

    return 0;
}

九、小结

代理模式通过引入代理对象,灵活地控制目标对象的访问权限,同时为目标对象添加额外功能而无需修改其代码。其核心在于间接性和扩展性,适用于权限管理、延迟加载和功能增强等场景。

你可能感兴趣的:(C++学习,代理模式,c++,开发语言)