《设计模式》之七:代理模式

Proxy Pattern 代理模式,也叫委托模式 是一个使用率非常高的模式,非常典型的场景就是游戏代练,代练者就是一个代理者或者委托者。其定义如下:

Provide a surrogate or placeholder for another object to control access to it

主要是三个角色:

1,Subject抽象主题角色,可以是抽象类也可以是接口

2,RealSubject具体主题角色,也叫被委托角色、被代理角色,他才是真正的业务逻辑具体执行者

3,Proxy代理主题角色,也叫委托类、代理类,负责对真实角色的应用

 

通用源代码:

/**
 * 抽象主题类
 *
 * @author XiongNeng
 * @version 1.0
 * @since 13-5-23
 */
public interface Subject {
    // 定义一个方法
    void request();
}

 

一个实现类:

public class RealSubject implements Subject {
    @Override
    public void request() {
        // 核心业务逻辑
    }
}

 

代理类:

public class Proxy implements Subject  {
    private Subject subject = null;
    public Proxy() {
        this.subject = new RealSubject();
    }
    public Proxy(Subject subject) {
        this.subject = subject;
    }
    @Override
    public void request() {
        before();
        this.subject.request();
        after();
    }

    // 预处理
    private void before() {
        System.out.println("before");
    }

    // 后处理
    private void after() {
        System.out.println("after");
    }
}

 

代理模式的优点:

1,职责清晰

真实的角色就是实现业务逻辑,不用关心其他非本职责事务,通过后期代理完成一件事务,附带结果就是编程简介清晰

2,高扩展性

具体主题随时会发生变化,只要它实现了接口,甭管它如何变化,都逃不出接口手掌,那么代理类完全可以不做任何修改下使用

3,智能化

动态代理你会看到的,Spring AOP是典型和经典的动态代理模式

 

代理是有个性的

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

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

 

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 upgrade() {
		this.gamePlayer.upgrade();
		count();
	}

	// 计算费用,这个是代理类的提供的功能
	public void count() {
		System.out.println("升级总费用是:200元");
	}

}

 

代理类可以为真实角色预处理消息、过滤消息、消息转发、事后处理消息等功能。当然一个代理类,可以代理多个真实角色,并且真实角色之间可以有耦合关系。

 

>>> 动态代理:

最后是我们的压轴大戏动态代理

动态代理在实现阶段不用关系代理谁,而是在运行阶段才指定代理哪一个对象。

InvocationHandler是JDK提供的动态代理接口,对被代理的方法进行代理。

public class GamePlayerIH implements InvocationHandler {
	// 被代理者
	Class cls = null;
	// 被代理的实例
	Object obj = null;
	// 我要代理谁
	public GamePlayerIH(Object _obj) {
		this.obj = _obj;
	}
	// 调用被代理的方法
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result = method.invoke(this.obj, args);
		return result;
	}
}

 通过InvocationHandler接口,所有的方法都由Handler来进行处理,invoke方法为必须实现的方法。

下面是一个简单的场景使用:

public class Client {
	public static void main(String[] args) {
		// 定义一个痴迷玩家
		IGamePlayer player = new GamePlayer("张三");
		// 定义一个Handler
		InvocationHandler handler = new GamePlayerIH(player);
		// 开始打游戏
		System.out.println("开始时间是: 2013-05-23 12:34");
		// 获得类的class loader
		ClassLoader cl = player.getClass().getClassloader();
		// 动态产生一个代理者
		IGamePlayer proxy = (IGamePlayer)proxy.newProxyInstance(cl, new Class[] {IGamePlayer.class}, handler);
		// 登录
		proxy.login("zhangsan", "password");
		// 开始杀怪
		proxy.killBoss();
		// 升级
		proxy.upgrade();
		// 记录结束时间
		System.out.println("结束时间是:2013-05-24 11:10");
	}
}

 

别急,动态代理最强大的地方是注入,如果上面的游戏玩家每次登陆后需要发一个信息给我吗,防止账号被盗,直接修改GamePlayer不是个好方法。

public class GamePlayerIH implements InvocationHandler {
	// 被代理者
	Class cls = null;
	// 被代理的实例
	Object obj = null;
	// 我要代理谁
	public GamePlayerIH(Object _obj) {
		this.obj = _obj;
	}
	// 调用被代理的方法
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result = method.invoke(this.obj, args);
		// 如果是登录方法,则发送信息
		if (method.getName().equalsIgnoreCase("login")) {
			System.out.println("账号登录了");
		}
		return result;
	}
}

 

AOP编程没有使用什么新的技术,但是它对我们的设计、编码有非常大的影响,对于日志、事务、权限等都可以在系统设计阶段不同考虑,而在设计后通过AOP的方式切过去。

 

于是我们知道,一个类的动态代理类是这样一个类,由InvocationHandler的实现类实现所有的方法,由其invoke方法接管所有方法的实现。

 

一个简单的AOP框架,可以在此基础上进行扩展:

抽象主题类Subject:

/**
 * 抽象主题类
 *
 * @author XiongNeng
 * @version 1.0
 * @since 13-5-23
 */
public interface Subject {
    // 定义一个方法
    void request();
}

 真实主题:

/**
 * 真实主题
 *
 * @author XiongNeng
 * @version 1.0
 * @since 13-5-23
 */
public class RealSubject implements Subject {
    @Override
    public void request() {
        // 核心业务逻辑
    }
}

 动态代理的Handler类:

public class MyInvocationHandler implements InvocationHandler {
	// 被代理的实例
	Object obj = null;
	// 我要代理谁
	public GamePlayerIH(Object _obj) {
		this.obj = _obj;
	}
	// 调用被代理的方法
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result = method.invoke(this.obj, args);
		return result;
	}
}

 动态代理类:

public class DynamicProxy<T> {
	public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
		// 寻找JoinPoint连接点,AOP框架使用元数据定义
		if (true) {
			// 执行一个前置通知
			(new BeforeAdvice()).exec();
		}
		// 执行目标,并返回结果
		return (T) Proxy.newProxyInstance(loader, interfaces, h);
	}
}

 

上面的DynamicProxy只是一个通用的类,不具有业务含义,我们可以实现一个具有业务含义的动态代理类

public class SubjectDynamicProxy extends DynamicProxy {
	public static <T> T newProxyInstance(Subject subject) {
		// 获得Classloader
		Classloader loader = subject.getClass().getClassLoader();
		// 获得接口数组
		Class<?>[] classes = subject.getClass().getInterfaces();
		// 获得handler
		InvocationHandler handler = new MyInvocationHandler(subject);
		return newProxyInstance(loader, classes, handler);
	}
}

 

这样一来,使用起来就非常简单了。

 

最佳实践:

代理模式使用非常广泛,大到系统框架、企业平台,小到代码片段、事务处理,稍不留意就用到代理模式。

有了Spring AOP 和 AspectJ这样优秀的工具,就更不用讲了。

 

AOP框架中,有几个概率一定要弄清楚:

切面 Aspect

切入点 JoinPoint

通知 Advice

织入 Weave

这几个概率请参考我的另一篇文章:

http://yidao620c.iteye.com/blog/1837689

 

本人博客已搬家,新地址为:http://yidao620c.github.io/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值