[C++实现 设计模式(6)] : 代理模式

16 篇文章 0 订阅

情景分析

       在魔兽世界的广为流传着这样一句话 , 当你满级的时候 , 这个游戏开算开始 . 然而为令人头疼的就是练级 , 想想现在满级已经120级了 , 要是申请个新号 , 这得练到什么时候 . 所以果断去找代练 , 将我们的账号交给他们 , 由他们去帮我升级 , 打怪 .于是我们得到下面的类图 :
在这里插入图片描述

       类图中的GamePlayerProxy类表示游戏的代练者 , 他也不能作弊 , 游戏代练者也是动手打怪 , 因此同样继承IGamePlayer接口(登录 , 杀怪 , 升级)

代码实现 :

#include<iostream>
#include<string>
//#include<vld.h>
using namespace std;


//代理模式
#if 0
//游戏者接口
class IGamePlayer
{
public:
	//登陆游戏 
	virtual void login(string user,string passwrd)  = 0;

	//杀怪 , 网络游戏的主要特色
	virtual void killBoss() = 0;

	//升级
	virtual void upgrade() = 0;

	//不将析构函数定义成虚函数 , 会造成内存泄漏
	virtual ~IGamePlayer() 
	{}

};

//游戏者
class GamePlayer : public IGamePlayer
{
public:
	//通过构造函数传递名称
	GamePlayer(string name)
	{
		_name = name;
	}


	//打怪 , 最期望的就是杀老怪
	void killBoss()
	{
		cout<<_name+" 在打怪!"<<endl;
	}

	//打游戏之前你肯定登录吧 , 这是一个必须要条件
	void login(string user  , string password)
	{
		cout<<"登录名为"+user+"的用户: "+_name+" 登录成功!"<<endl;
	}

	//升级 , 升级有很多种方法 , 花钱买是一种,  做任务也是一种
	void upgrade()
	{
		cout<<_name+" 又升了一级!"<<endl;
	}
private:
	string _name;
};

//代练者
class GamePlayerProxy : public IGamePlayer
{
public:
	//通过构造函数传递要对谁进行代练
	GamePlayerProxy(IGamePlayer* gamePlayer = nullptr)
	{
		_gamePlayer = gamePlayer;
	}

	//代练杀怪
	void killBoss()
	{
		_gamePlayer->killBoss();
	}

	//代练登录
	void login(string user , string password)
	{
		_gamePlayer->login(user,password);
	}

	//代练升级
	void upgrade()
	{
		_gamePlayer->upgrade();
	}

private:
	IGamePlayer* _gamePlayer;
};

int main()
{
	//定义一个痴迷的玩家
	IGamePlayer* player = new GamePlayer("张三");
	//然后再定义一个代练者
	IGamePlayer* proxy = new GamePlayerProxy(player);
	//开始打游戏 , 记下时间戮
	cout<<"开始时间是 : 2019-5-30 15:42"<<endl;
	proxy->login("zhangsan","password");
	//开始杀怪
	proxy->killBoss();
	//升级
	proxy->upgrade();
	//记录结束时间
	cout<<"结束时间是 : 2019-5-31 12:00"<<endl;


	delete player;
	delete proxy;
	return 0;
}

#endif

运行结果 :
在这里插入图片描述

代理模式的定义

定义 : 为其他对象提供一种代理以控制对这个对象的访问

代理模式的通用类图 :
在这里插入图片描述

       代理模式也叫做委托模式,它是一项基本设计技巧 . 许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式而且在日常的应用中,代理模式可以提供非常好的访问控制 . 在一些著名开源软件中也经常见到它的身影,如Struts2的Form元素映射就采用了代理模式(准确地说是动态代理模式) . 我们先看一下类图中的三个角色的定义:

  • Subject抽象主题角色
    抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求 .
  • RealSubject具体主题角色
    也叫做被委托角色、被代理角色 . 它才是冤大头,是业务逻辑的具体执行者 .
  • Proxy代理主题角色
    也叫做委托类、代理类 . 它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作 .

代理模式的应用

代理模式的优点
  • 职责清晰
    真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰 .
  • 高扩展性
    具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用 .
  • 智能化
    有兴趣的读者也可以看看Struts是如何把表单元素映射到对象上的 .
代理模式的使用场景

       我相信第一次接触到代理模式的读者肯定很郁闷,为什么要用代理呀?想想现实世界吧,打官司为什么要找个律师?因为你不想参与中间过程的是是非非,只要完成自己的答辩就成,其他的比如事前调查、事后追查都由律师来搞定,这就是为了减轻你的负担 .

代理模式的扩展

普通代理

       我们设计模式中的普通代理和强制代理也是类似的一种结构,普通代理就是我们要知道代理的存在,也就是类似的GamePlayerProxy这个类的存在,然后才能访问;强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的 .

       普通代理,它的要求就是客户端只能访问代理角色,而不能访问真实角色 . 我们以上面的例子作为扩展,我自己作为一个游戏玩家,我肯定自己不练级了,也就是场景类(main())不能再直接new一个GamePlayer对象了,它必须由GamePlayerProxy来进行模拟场景,类图修改如图:
在这里插入图片描述

       仅仅修改了两个实现类的构造函数,GamePlayer的构造 函数增加了_gamePlayer参数,而代理角色则只要传人代理者名字即可,而不需要说是替哪个对象做代理 .
实现代码 :

#include<iostream>
#include<string>
#include<vld.h>
using namespace std;


//普通代理模式
#if 0
//游戏者接口
class IGamePlayer
{
public:
	//登陆游戏 
	virtual void login(string user,string passwrd)  = 0;

	//杀怪 , 网络游戏的主要特色
	virtual void killBoss() = 0;

	//升级
	virtual void upgrade() = 0;

		
	//不将析构函数定义成虚函数 , 会造成内存泄漏
	virtual ~IGamePlayer() 
	{}

};

//普通代理的游戏者
class GamePlayer : public IGamePlayer
{
public:
	//通过构造函数传递名称
	GamePlayer(IGamePlayer* _gamePlayer ,  string name)
	{
		if(_gamePlayer == nullptr)
		{
			cout<<"不能创建真实角色!"<<endl;
		}
		else
		{
			_name = name;
		}
	}

	//打怪 , 最期望的就是杀老怪
	void killBoss()
	{
		cout<<_name+" 在打怪!"<<endl;
	}

	//打游戏之前你肯定登录吧 , 这是一个必须要条件
	void login(string user  , string password)
	{
		cout<<"登录名为"+user+"的用户: "+_name+" 登录成功!"<<endl;
	}

	//升级 , 升级有很多种方法 , 花钱买是一种,  做任务也是一种
	void upgrade()
	{
		cout<<_name+" 又升了一级!"<<endl;
	}
private:
	string _name;
};

//普通代理的代练者
class GamePlayerProxy : public IGamePlayer
{
public:
	//通过构造函数传递要对谁进行代练
	GamePlayerProxy(string name)
	{
		try
		{
			_gamePlayer = new GamePlayer(this,name);
		}
		catch(exception e)
		{}
	}
	~GamePlayerProxy()
	{
		delete _gamePlayer;
	}

	//代练杀怪
	void killBoss()
	{
		_gamePlayer->killBoss();
	}

	//代练登录
	void login(string user , string password)
	{
		_gamePlayer->login(user,password);
	}

	//代练升级
	void upgrade()
	{
		_gamePlayer->upgrade();
	}

private:
	IGamePlayer* _gamePlayer ;
};

int main()
{
	//然后再定义一个代练者
	IGamePlayer* proxy = new GamePlayerProxy("张三");
	//开始打游戏 , 记下时间戮
	cout<<"开始时间是 : 2019-5-30 15:42"<<endl;
	proxy->login("zhangsan","password");
	//开始杀怪
	proxy->killBoss();
	//升级
	proxy->upgrade();
	//记录结束时间
	cout<<"结束时间是 : 2019-5-31 12:00"<<endl;

	delete proxy;

	return 0;
}

#endif

运行结果 :

在这里插入图片描述

       运行结果完全相同 . 在该模式下,调用者只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响真实的主题角色想怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合 . 当然,在实际的项目中,一般都是通过约定来禁止new一个真实的角色,这也是一个非常好的方案 .

       注意 : 普通代理模式的约束问题,尽量通过团队内的编程规范类约束,因为每一个主题类是可被重用的和可维护的,使用技术约束的方式对系统维护是一种非常不利的因素 .

强制代理

       一般的思维都是通过代理找到真实的角色 , 但是强制代理却是要" 强制 " , 你必须通过真实角色查找到代理角色 , 否则你不能访问 . 只有通过真实角色指定的代理类才可以访问 , 也就是说由真实角色管理代理角色 .
强制代理类图 :
在这里插入图片描述

代码实现 :

#include<iostream>
#include<string>
#include<vld.h>
using namespace std;

//游戏者接口
class IGamePlayer
{
public:
	//登陆游戏 
	virtual void login(string user,string passwrd)  = 0;

	//杀怪 , 网络游戏的主要特色
	virtual void killBoss() = 0;

	//升级
	virtual void upgrade() = 0;
	
	//每个人可以找一下自己的代理
	virtual IGamePlayer* getProxy() = 0;

		
	//不将析构函数定义成虚函数 , 会造成内存泄漏
	virtual ~IGamePlayer() 
	{}

	
};

//强制代理的代练者
class GamePlayerProxy : public IGamePlayer
{
public:
	//通过构造函数传递要对谁进行代练
	GamePlayerProxy(IGamePlayer* gamePlayer = nullptr)
	{
		_gamePlayer = gamePlayer;
	}

	//代练杀怪
	void killBoss()
	{
		_gamePlayer->killBoss();
	}

	//代练登录
	void login(string user , string password)
	{
		_gamePlayer->login(user,password);
	}

	//代练升级
	void upgrade()
	{
		_gamePlayer->upgrade();
	}

	//代理的代理暂时还没有 , 就是自己
	IGamePlayer* getProxy()
	{
		return this;
	}

private:
	IGamePlayer* _gamePlayer ;
};

//强制代理的真实角色
class GamePlayer : public IGamePlayer
{
public:
	//通过构造函数传递名称
	GamePlayer(string name)
		: _proxy(nullptr)
	{
		_name = name;
	}
	

	//找到自己的代理
	IGamePlayer* getProxy()
	{
		_proxy = new GamePlayerProxy(this);
		return _proxy;
	}

	//打怪 , 最期望的就是杀老怪
	void killBoss()
	{
		if(isProxy())
		{
			cout<<_name+" 在打怪!"<<endl;
		}
		else
		{
			cout<<"请使用指定的代理访问"<<endl;
		}
	}

	//打游戏之前你肯定登录吧 , 这是一个必须要条件
	void login(string user  , string password)
	{
		if(isProxy())
		{
			cout<<"登录名为"+user+"的用户: "+_name+" 登录成功!"<<endl;
		}
		else
		{
			cout<<"请使用指定的代理访问"<<endl;
		}
	}

	//升级 , 升级有很多种方法 , 花钱买是一种,  做任务也是一种
	void upgrade()
	{
		if(isProxy())
		{
			cout<<_name+" 又升了一级!"<<endl;
		}
		else
		{
			cout<<"请使用指定的代理访问"<<endl;
		}
	}
private:
	string _name;
	IGamePlayer* _proxy;
	bool isProxy()
	{
		if(_proxy == nullptr)
		{
			return false;
		}
		else
		{
			return true;
		}
	}
};

我们先按照常规思路来运行一下 , 直接new一个真实的角色

//强制代理的错误场景1
#if 0
int main()
{
	//定义一个痴迷的玩家
	IGamePlayer* player = new GamePlayer("张三");
	//开始打游戏 , 记下时间戮
	cout<<"开始时间是 : 2019-5-30 15:42"<<endl;
	player->login("zhangsan","password");
	//开始杀怪
	player->killBoss();
	//升级
	player->upgrade();
	//记录结束时间
	cout<<"结束时间是 : 2019-5-31 12:00"<<endl;

	delete player;

	return 0;
}

#endif

运行结果 :
在这里插入图片描述

        它要求你必须通过代理来访问 , 你想要直接访问它 , 门儿都没有 . 那我们接着通过代理来访问 , 那就产生一个代理

//强制代理的错误场景2
#if 0
int main()
{
	//定义一个痴迷的玩家
	IGamePlayer* player = new GamePlayer("张三");
	//再定义一个代练者
	IGamePlayer* proxy = new GamePlayerProxy(player);
	//开始打游戏 , 记下时间戮
	cout<<"开始时间是 : 2019-5-30 15:42"<<endl;
	proxy->login("zhangsan","password");
	//开始杀怪
	proxy->killBoss();
	//升级
	proxy->upgrade();
	//记录结束时间
	cout<<"结束时间是 : 2019-5-31 12:00"<<endl;

	delete player;
	delete proxy;

	return 0;
}

#endif

运行结果 :
在这里插入图片描述

       还是不能访问,为什么呢? 它不是真实角色指定的对象,这个代理对象是你自己new出来的,当然真实对象不认了,这就好比是那个明星,人家已经告诉你去找她的代理人了,你随便找个代理人能成吗?你必须去找她指定的代理才成! 那我们再来修改一下

int main()
{
	//定义一个痴迷的玩家
	IGamePlayer* player = new GamePlayer("张三");
	//注意!  获得指定的代理
	IGamePlayer* proxy = player->getProxy();
	//开始打游戏 , 记下时间戮
	cout<<"开始时间是 : 2019-5-30 15:42"<<endl;
	proxy->login("zhangsan","password");
	//开始杀怪
	proxy->killBoss();
	//升级
	proxy->upgrade();
	//记录结束时间
	cout<<"结束时间是 : 2019-5-31 12:00"<<endl;

	delete player;
	delete proxy;

	return 0;
}

运行结果 :
在这里插入图片描述

       OK , 可以正常访问了 . 强制代理的概念就是要从真实角色查找到代理角色 , 不允许直接访问真实角色.

参考书籍 :

                  <<设计模式之禅 第二版>>
                  <<设计模式>>

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值