java设计模式——代理模式的应用与扩展(2)

本文主要参考资料:《设计模式之禅》

接着《设计模式——代理模式的定义(1)》继续讲

目录

1)代理模式的应用

2)代理模式的扩展

一. 代理模式的应用

1.代理模式的优点

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

2.代理模式的应用

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

二. 代理模式的扩展

1.普通代理

       在网络上代理服务器设置分为透明代理和普通代理,是什么意思呢?透明代理就是用户不用设置代理服务器地址,就可以直接访问,也就是说代理服务器对用户来说透明的,看不到,不用知道它存在的;普通代理则是需要用户自己设置代理服务器的IP地址,用户必须知道代理的存在。我们设计模式中的普通代理和强制代理也是类似的一种结构,普通代理就是我们要知道代理的存在,也就是类似的GamePlayerProxy这个类的存在,然后才能访问;强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的,这样解释还是比较复杂,我们还是用实例来讲解。

      首先说普通代理,它的要求就是客户端只能访问代理角色,而不能访问真实角色。这个比较简单,我们还是以打游戏例子进行扩展。我么自己作为一个游戏玩家,我肯定自己不练级了,也就是场景类不能再直接new一个GamePlayer对象了,他必须由GamePlayerProxy来进行模拟场景,类图修改如下:

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

public interface IGamePlayer {

	//登录游戏
	public void login(String user,String password);
	
	//杀怪,这是网络游戏的主要特色
	public void killBoss();
	
	//升级
	public void upgrade();
}
public class GamePlayer implements IGamePlayer {
	private String name = "";
	
	//构造函数限制谁能创建对象,并同时传递姓名
	public GamePlayer(IGamePlayer _gamePlayer,String _name) throws Exception{
		if(_gamePlayer == null ){
			throw new Exception("不能创建真是角色!");
		}else{
			this.name = _name;
		}
		
	}
	//打怪,最期望的就是杀老怪
	public void killBoss() {
		System.out.println(this.name + "在打怪!");
	}
	
	//进游戏之前你肯定要登录吧,这是一个必要条件
	public void login(String user, String password) {
		System.out.println("登录名为"+user + " 的用户 " + this.name + "登录成功!");
	}

	//升级,升级有很多方法,花钱买是一种,做任务也是一种
	public void upgrade() {
		System.out.println(this.name + " 又升了一级!");
	}
}

 在构造函数中传递进来一个IGamePlayer对象,检查谁能创建真实的角色(当然还可以有其他限制,比如类名必须是Proxy,我们可以根据实际情况进行扩展)

public class GamePlayerProxy implements IGamePlayer {
	private IGamePlayer gamePlayer = null;
	
	//通过构造函数传递要对谁进行代练
	public GamePlayerProxy(String name){
		try {
			gamePlayer = new GamePlayer(this,name);
		} catch (Exception e) {
			// TODO 异常处理
		}
	}
	
	//代练杀怪
	public void killBoss() {
		this.gamePlayer.killBoss();
	}

	//代练登录
	public void login(String user, String password) {
		this.gamePlayer.login(user, password);

	}

	//代练升级
	public void upgrade() {
		this.gamePlayer.upgrade();

	}
}

仅仅修改了构造函数,传递进来一个代理者名字,即可代理。调用者只知道代理存在即可,不用知道代理了谁

public class Client {

	public static void main(String[] args) {

		//然后再定义一个代练者
		IGamePlayer proxy = new GamePlayerProxy("张三");
		
		//开始打游戏,记下时间戳
		System.out.println("开始时间是:2009-8-25 10:45");
		proxy.login("zhangSan", "password");
		//开始杀怪
		proxy.killBoss();
		//升级
		proxy.upgrade();
		//记录结束游戏时间
		System.out.println("结束时间是:2009-8-26 03:40");
		
	}
}

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

2.强制代理

       强制代理在设计模式中比较另类,为什么这么说呢?一般的思维都是通过代理找到真实的角色,但是强制代理却是要“强制”,你必须通过真实角色查找到代理角色,否则你不能访问,甭管你是通过代理类还是通过直接new一个主题角色类,都不能访问,只有通过真实角色指定的代理类才可以访问,也就是说由真实角色管理代理角色,这么说吧,高层模块new了一个真实角色的对象,返回的却是代理角色。

        我们还是使用上面打游戏的例子把。我们把打游戏的例子类图稍微改一下:

在接口上增加一个getProxy方法,真实角色GamePlayer可以指定一个自己的代理,除了代理外谁都不能访问。我们来看代码:

public interface IGamePlayer {

	//登录游戏
	public void login(String user,String password);
	
	//杀怪,这是网络游戏的主要特色
	public void killBoss();
	
	//升级
	public void upgrade();
	
	//每个人都可以找一下自己的代理
	public IGamePlayer getProxy();
}

IGamePlayer增加一个getProxy方法,指定要访问自己必须通过哪个代理。 

public class GamePlayer implements IGamePlayer {
	private String name = "";
	//我的代理是谁
	private IGamePlayer proxy = null;
		
	public GamePlayer(String _name){
		this.name = _name;	
	}
	
	//找到自己的代理
	public IGamePlayer getProxy(){
		this.proxy = new GamePlayerProxy(this);
		return this.proxy;
	}
	
	//打怪,最期望的就是杀老怪
	public void killBoss() {
		if(this.isProxy()){
			System.out.println(this.name + "在打怪!");
		}else{
			System.out.println("请使用指定的代理访问");
		}
		
	}
	
	//进游戏之前你肯定要登录吧,这是一个必要条件
	public void login(String user, String password) {
		if(this.isProxy()){
			System.out.println("登录名为"+user + " 的用户 " + this.name + "登录成功!");
		}else{
			System.out.println("请使用指定的代理访问");;
		}
		
	}

	//升级,升级有很多方法,花钱买是一种,做任务也是一种
	public void upgrade() {
		if(this.isProxy()){
			System.out.println(this.name + " 又升了一级!");
		}else{
			System.out.println("请使用指定的代理访问");
		}
	}
	
	//校验是否是代理访问
	private boolean isProxy(){
		if(this.proxy == null){
			return false;
		}else{
			return true;
		}
	}
}

GamePlayer增加一个私有方法,检查是否是自己指定的代理,是制定的代理怎允许访问,否则不允许. 

public class GamePlayerProxy implements IGamePlayer {
	private IGamePlayer gamePlayer = null;
	
	//构造函数传递用户名
	public GamePlayerProxy(IGamePlayer _gamePlayer){	
		this.gamePlayer = _gamePlayer;
	}
	
	//代练杀怪
	public void killBoss() {
		this.gamePlayer.killBoss();
	}

	//代练登录
	public void login(String user, String password) {
		this.gamePlayer.login(user, password);

	}

	//代练升级
	public void upgrade() {
		this.gamePlayer.upgrade();

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

3. 代理是有个性的

       一个类可以实现多个接口,完成不同任务的整合,那也就是说代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截和过滤。例如游戏代理是需要收费的,升一级需要5元钱,这个计算功能就是代理类的个性,它应该在代理的接口中定义。

增加一个IProxy接口,其作用是计算代理的费用。我们先来看IProxy接口代码:

public interface IProxy {
	//计算费用
	public void count();
}

然后再来看看GamePlayerProxy带来的变化:

public class GamePlayerProxy implements IGamePlayer,IProxy {
	private IGamePlayer gamePlayer = null;
	
	//通过构造函数传递要对谁进行代练
	public GamePlayerProxy(IGamePlayer _gamePlayer){
		this.gamePlayer = _gamePlayer;
	}
	
	//代练杀怪
	public void killBoss() {
		this.gamePlayer.killBoss();
	}

	//代练登录
	public void login(String user, String password) {
		this.gamePlayer.login(user, password);

	}

	//代练升级
	public void upgrade() {
		this.gamePlayer.upgrade();
		this.count();
	}
	
	//计算费用
	public void count(){
		System.out.println("升级总费用是:150元");
	}
}

实现了IProxy接口,同时在upgrade方法中调用该方法,完成费用结算,其他类没有任何改动,运行结果如下:

好了,代理公司也赚钱了,我的游戏也升级了,皆大欢喜。代理类不仅仅是可以有自己的运算方法,通常的情况下代理的职责并不单一,它可以组合其他真实角色,也可以实现自己的职责,比如计算费用。代理类可以为真实角色预处理消息,过滤消息,消息转发,事后处理消息等功能。当然一个代理类,可以代理多个真实角色,并且真实角色之间可以有耦合关系。

4. 虚拟代理

猜你喜欢

转载自blog.csdn.net/qq_36582604/article/details/82288618