本文记录结构型模式之代理模式。
代理(Proxy)模式 是一种结构型模式,在很多不同的场合具有广泛的分类和应用。其主要实现思想是在客户端和真正要访问的对象之间引人一个代理对象(中间层), 于是, 以客户端对真正对象的访问,现在变成了通过代理对象进行访问,代理对象在这里起到了一个中介或者桥梁作用。引入代理对象的主要目的是可以为客户端增加额外的功能、约束或针对客户端的调用屏蔽一些复杂的细节问题。

基本概念和范例

代理模式的实质是通过引入一个代理类来为原始类(被代理类)增加额外的能力,这些额外的能力可能是指一些新功能、新服务,也可能是一些约束或限制(例如,只有特定用户才能使用原始类)等。
下面的示例:

    // 
    class WebAddress
    {
    public:
        virtual void visit() = 0;

        virtual ~WebAddress()
        {

        }
    };

    // 购物网站
    class WebAddressShopping : public WebAddress
    {
    public:
        virtual void visit()
        {
            cout << "访问WebAddressShopping 购物网站!" << endl;
        }
    };

    // 视频网站
    class WebAddressVideo : public WebAddress
    {
    public:
        virtual void visit()
        {
            cout << "访问 WebAddressVideo 视频网站" << endl;
        }
    };

    // 网络代理类:这是一个中间类
    class WebAddressShoppingProxy : public WebAddress
    {
    public:
        virtual void visit()
        {
            // 做日志记录或者流量限制

            // 其他记录

            WebAddress* p = new WebAddressShopping();
            p->visit();

            delete p;
        }
    };

    void test()
    {
        WebAddressShoppingProxy* p = new WebAddressShoppingProxy();
        p->visit();
    }

上边的代码可能十分复杂,甚至可能借助专业工具来完成,而且往往使用这个代理类的程序员与开发这个类的程序员不是同一个,甚至是两个人程序员。下面举一个例子:
假设A公司有一个内部Web网站(图中最右侧),该Web网站提供一个Visit接口, A公司的对外业务服务器程序可以以网络通信方式触发对该Web网站visit 接口调用,以获取该网站中的信息。
A公司允许其他公司通过对外业务服务器程序获取内部Web网站的信息,但每个月要收取一定的费用并且有一定的流量限制。
假设B公司的程序员,负责开发一个应用程序来获取A公司内部的Web网站的信息,但是B公司要给A公司支付一定的费用,A公司给B公司一个账号,并为该公司提供一个库文件,B公司的程序员根据A公司提供的开发文档使用A公司的服务,假设这个库文件就是一个代理类,WebAddressShoppingProxy()。
在这里插入图片描述
通过上面的示例可以看出,代理类对原来类进行了扩展。

引入代理(Proxy)模式

“代理”设计模式的定义(实现意图): 为其他对象提供一种代理,以控制对这个对象的访问。
代理模式的UML图如下:
在这里插入图片描述
(1) Subject(抽象主题)。该类定义真实主题与代理主题的共同接口,这样,在使用真实主题的地方都可以使用代理主题,但是后面会讲到,因为代理的种类繁多,目的各异,所以代理主题并不一定必须与真实主题有共同的接口甚至抽象主题也不是必须存在的,这一点读者灵活理解。这里指WebAddr类。
(2) Proxy(代理主题)。该类内部包含了对真实主题的引用,从而可以对真实主题进行访问。
(3) RealSubject(真实主题)。定义代理主题所代表的真实对象,真正的业务是在真实题中实现的,客户端(例如main主函数中)通过代理主题间接访问真实主题中的接口。

代理模式的应用场合探究

代理模式常用应用场景

下面说说代理模式具体的应用场景。
远程代理(Remote Proxy,类似于翻墙): 为一个在不同地址空间的对象提供一个局部代表(本地代理对象)。这里不同的地址空间可以是同一个计算机上的两个不同程序,也可以是运行在两个不同计算机上的通过网络通信的程序(例如,可以通过socket实现网络通信)。
在这里插入图片描述
虚拟代理:

保护代理:

缓存/缓冲代理:

智能引用代理:

写时复制优化代理:

缓存/缓冲代理(Cache Proxy)一例

下面实现一个代理主题的例子。下面的例子中,代理主题存在的意义就是避免了频繁调用真实主题的read()方法来获取1.txt 中的内容,减少了磁盘IO, 从而提高的程序效率。

	vector<string> g_fileItemList;

	// 抽象主题
	class ReadInfo
	{
	public:
		virtual void read() = 0;
		virtual ~ReadInfo() {}
	};

	// 真实主题
	class ReadInfoFromFile : public ReadInfo
	{
	public:
		virtual void read() // 从文件中读信息(读取test.txt的内容)
		{
			ifstream fin("1.txt");
			if (!fin)
			{
				cout << "文件代开失败" << endl;
			}
			string linebuf;
			while (getline(fin, linebuf))  // 遇到换行符 EOF 结束;每次读取时,清除linebuf的内容并将新内容存入linebuf
			{
				if (!linebuf.empty())
				{
					g_fileItemList.push_back(linebuf);
				}
			}
			fin.close();
		}
	};

	// 代理主题
	class ReadInfoProxy :public ReadInfo
	{
	public:
		virtual void read()
		{
			if (!m_bRead)
			{
				m_bRead = true;
				cout << "从文件中读取内容" << endl;
				ReadInfoFromFile *p = new ReadInfoFromFile();
				p->read();
				delete p;
			}
			else
			{
				cout << "已经从文件中读取过内容" << endl;
			}

			// 输出数据
			for (auto v : g_fileItemList)
			{
				cout << v << endl;
			}
		}

	private:
		bool m_bRead = false; // 是否已经读取过

	};

	void test()
	{
		ReadInfoProxy *p = new ReadInfoProxy();
		p->read();
		p->read();
		p->read();
	}

在这里插入图片描述

总结:一行一行读文件总结

        virtual void read() override
        {
            ifstream fin("1.txt");
            if (!fin)
            {
                cout << "文件打开失败" << endl;
            }

            string lineBuf;
            while (getline(fin, lineBuf))  // 遇到EOF结束,每次读取一行
            {
                if (!lineBuf.empty())
                {
                    g_fileItemList.push_back(lineBuf);
                }
            }

            fin.close();
        }

打开文件,使用ifstream对象;判断对象是否有效,!fin;
使用getline方法读取一行,放入lineBuf中;getline(fin,lineBuf)读取一行。
在使用getline()时要和while()一起使用,遇到EOF才会结束。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐