c++设计模式:代理模式

前期知识:

代理模式是常用的结构型设计模式之一,当无法直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,所访问的真实对象与代理对象需要实现相同的接口。

根据代理模式的使用目的不同,代理模式又可以分为多种类型,例如保护代理、远程代理、虚拟代理、缓冲代理等,它们应用于不同的场合,满足用户的不同需求。


代理模式概述

代理模式是一种应用很广泛的结构型设计模式,而且变化形式非常多,常见的代理形式包括远程代理、保护代理、虚拟代理、缓冲代理、智能引用代理等,后面将学习这些不同的代理形式。


代理模式定义如下:

代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。

代理模式是一种对象结构型模式。在代理模式中引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到中介的作用,它去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务。

所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网上连接、存储器中的大对象、文件或其它昂贵或无法复制的资源。


代理模式结构与实现

模式结构

代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层,代理模式结构如下图所示:

代理模式包含如下三个角色:

Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。

Proxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。

RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。

简要总结如下:

抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
模式实现

代理模式的结构图比较简单,但是在真实的使用和实现过程中要复杂很多,特别是代理类的设计和实现。

抽象主题类声明了真实主题类和代理类的公共方法,它可以是接口、抽象类或具体类。


客户端针对抽象主题类编程,一致性地对待真实主题和代理主题,典型的抽象主题类代码如下:

class Subject
{
public:
    virtual void Request() = 0;
}

真实主题类继承了抽象主题类,提供了业务方法的具体实现,其典型代码如下:

class RealSubject : public Subject
{
public:
    void Request()
    {
        // 业务方法具体实现代码
    }
}

代理类也是抽象主题类的子类,它维持一个对真实主题对象的引用,调用在真实主题中实现的业务方法,在调用时可以在原有业务方法的基础上附加一些新的方法来对功能进行扩充或约束,最简单的代理类实现代码如下:

class Proxy : public Subject
{
public:
    Procy()
    {
        realSubject = new RealSubject(); //维持一个对真实主题对象的引用
    }
    void PreRequest() 
    {
       ···
    }
 
    void Request() 
    {
        PreRequest();
        realSubject.Request(); //调用真实主题对象的方法
        PostRequest();
    }
 
    void PostRequest() 
    {
        ……
    }
}
private:
    RealSubject realSubject;

在实际开发过程中,代理类的实现比上述代码要复杂很多,代理模式根据其目的和实现方式不同可分为很多种类,其中常用的几种代理模式简要说明如下:

远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使(Ambassador)。
虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等。

在这些常用的代理模式中,有些代理类的设计非常复杂,例如远程代理类,它封装了底层网络通信和对远程对象的调用,其实现较为复杂。


代理模式的应用实例:模拟登录系统

我们先来看一个简单的框架实现:

/**
* Copyright: Copyright(c) 2019
* Created on 26/4/2019
* Author : ZYZMZM
* Version 1.0
* Title : Proxy Pattern
**/

#include <iostream>
#include <string>
using namespace std;
/* 定义接口:抽象主题角色 */
class Interface
{
public:
    virtual void Request() = 0;
};

/* 真实类 : 真实主题角色 */
class RealClass : public Interface
{
public:
    virtual void Request()
    {
        cout << "真实的请求" << endl;
    }
};

/* 代理类 :代理主题角色 */
class ProxyClass : public Interface
{
private:
    RealClass* m_realClass;
public:
    virtual void Request()
    {
        m_realClass = new RealClass();
        m_realClass->Request();
        delete m_realClass;
    }
};

/* 客户端需通过代理访问真实主题 */
int main()
{
    ProxyClass* test = new ProxyClass();
    test->Request();
    return 0;
}

通过上述程序可能会发现,先访问代理类再访问真正要访问的对象,似乎这样有点多此一举的味道,其实不然。

代理类可以在真正的类执行之前,进行预处理。比如你有一个系统实现了登陆功能,在用户登录时, 真正的登录类和代理登录类都实现了Login接口, 不同的是Proxy类的方法中增加了用户是否合法的判断, 只有合法时才去调用真正登录类的Login方法,用户访问的其实是Proxy的login方法,这都是代理模式的优点。而且采用代理模式的话,并且你可以随时更改代理。还有一点你会发现,真正对象与代理它们实现同一个接口。

那么接下来我们使用代理模式实现一个模拟登录功能:

/**
* Copyright: Copyright(c) 2019
* Created on 26/4/2019
* Author : ZYZMZM
* Version 1.0
* Title : Proxy Pattern —— Simulated Login
**/

#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;

/* 定义客户信息数据库 */
unordered_map<string, string> cliInfo;

/* 添加客户信息 */
void addCliInfo(string name, string passwd)
{
    cliInfo.insert(make_pair(name, passwd));
}

/* 删除指定客户信息 */
void delCliInfo(string name)
{
    cliInfo.erase(name);
    cout << endl << "Delete " << name << "'s Info" << endl << endl;
}

/* 显示数据库存储信息 */
void Show()
{
    cout << "DataBase Info:" << endl;
    auto it = cliInfo.begin();
    while (it != cliInfo.end())
    {
        cout << (*it).first << " : " << (*it).second << endl;
        ++it;
    }
    cout << endl;
}


/* 抽象主题角色 */
class Interface
{
public:
    virtual void Request() = 0;
};

/* 真实主题角色:用户登陆验证成功后才能进入 */
class RealClass : public Interface
{
public:
    RealClass(string name, string passwd)
        :mUserName(name), mPassword(passwd) {}

    virtual void Request()
    {
        cout << mUserName << " : Login Sucesses" << endl;
    }
private:
    string mUserName;
    string mPassword;
};

/* 代理主题角色:进行用户登陆验证,验证成功后才交付真实主题处理 */
class ProxyClass : public Interface
{
private:
    RealClass* m_realClass;
public:
    ProxyClass(string name, string passwd)
        :mUserName(name), mPassword(passwd) {}

    /* 验证用户输入的用户名和密码是否匹配 */
    bool PreRequest()
    {
        auto it = cliInfo.find(mUserName);
        if (it != cliInfo.end())
        {
            if (strcmp((*it).second.c_str(), mPassword.c_str()) == 0)
            {
                return true;
            }
            else
            {
                return false;
            }

        }
        return false;
    }

    /* 根据验证结果进行转发,成功转入真实主题角色中,错误直接返回 */
    virtual void Request()
    {
        if (PreRequest())
        {
            m_realClass = new RealClass(mUserName, mPassword);
            m_realClass->Request();
            delete m_realClass;
            return;
        }
        cout << mUserName << " : Login Failed" << endl;
    }
private:
    string mUserName;
    string mPassword;
};

/* 客户端需通过代理访问真实主题 */
int main()
{
    /* 向数据库中添加用户信息 */
    addCliInfo("zy", "123456");
    addCliInfo("yb", "678901");
    addCliInfo("ls", "535791");
    addCliInfo("zs", "438662");

    /* 显示数据库信息 */
    Show();

    /* 合法用户 */
    ProxyClass user1("zy", "123456");
    user1.Request();

    /* 非法用户:用户名错误 */
    ProxyClass user2("hahaha", "qwewqe");
    user2.Request();

    /* 合法用户 */
    ProxyClass user3("yb", "678901");
    user3.Request();

    /* 非法用户:密码错误 */
    ProxyClass user4("zs", "678901");
    user4.Request();

    /* 合法用户 */
    ProxyClass user5("ls", "535791");
    user5.Request();

    /* 将 yb 从数据库中删除 */
    delCliInfo("yb");

    /* 非法用户:数据库中已不存在 yb 用户 */
    ProxyClass user6("yb", "678901");
    user6.Request();

    cout << endl;

    /* 显示数据库信息 */
    Show();
}


代理模式的适用场景

代理模式是常用的结构型设计模式之一,它为对象的间接访问提供了一个解决方案,可以对对象的访问进行控制。代理模式类型较多,其中远程代理、虚拟代理、保护代理等在软件开发中应用非常广泛。


代理模式的共同优点如下:

能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。
此外,不同类型的代理模式也具有独特的优点,例如:

远程代理为位于两个不同地址空间对象的访问提供了一种实现机制,可以将一些消耗资源较多的对象和操作移至性能更好的计算机上,提高系统的整体运行效率。
虚拟代理通过一个消耗资源较少的对象来代表一个消耗资源较多的对象,可以在一定程度上节省系统的运行开销。
缓冲代理为某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,优化系统性能,缩短执行时间。
保护代理可以控制对一个对象的访问权限,为不同用户提供不同级别的使用权限。
模式缺点


代理模式的主要缺点如下:

由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,例如保护代理。
实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂,例如远程代理。
模式适用场景

代理模式的类型较多,不同类型的代理模式有不同的优缺点,它们应用于不同的场合:

当客户端对象需要访问远程主机中的对象时可以使用远程代理。
当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。
当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。
当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。
当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值