设计模式之禅--6.代理模式

代理模式式定义

(Provide a surrogate or placeholder for another object to control access to it.为其他对象提供一种代理以控制对这个对象的访问。)

代理模式包含如下角色:

ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。

RealSubject:真实主题角色,是实现抽象主题接口的类。

Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

实现动态代理的关键技术是反射。

静态代理:

代理模式有几种,虚拟代理,计数代理,动态代理。主要分为两类,静态代理和动态代理。静态代理比较简单,有程序员编写的代理类,并在程序运行前就编译好的,而不是有程序动态产生的。

动态代理

一般来说,对代理模式而言,一个主题类与一个代理类一一对应,这也是静态代理模式的特点

但是,也存在这样的情况,有n个主题类,但是代理类中的“前处理、后处理”都是一样的,仅调用主题不同。多个主题类对应一个代理类,共享“前处理,后处理”功能,动态调用所需主题,大大减少了程序规模,就是动态代理的特点。

代理模式的通用类图

演示代码

普通代理

我就以打游戏作为场景吧!首先我们定义一个痴迷的玩家接口

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(String _name) {
		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 + " 又升了一级!");
	}

}

痴迷的玩家一直打游戏打到自己坚持不住,可是又不愿意放弃自己的黄金段位,怎么办?那就花点钱找个代理替自己玩吧。

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 class Client {

	public static void main(String[] args) {
		// 定义一个痴迷的玩家
		IGamePlayer player = new GamePlayer("张三");

		// 然后再定义一个代练者
		IGamePlayer proxy = new GamePlayerProxy(player);

		// 开始打游戏,记下时间戳
		System.out.println("开始时间是:2009-8-25 10:45");
		proxy.login("zhangSan", "password");
		// 开始杀怪
		proxy.killBoss();
		// 升级
		proxy.upgrade();
		// 记录结束游戏时间
		System.out.println("结束时间是:2009-8-26 03:40");

	}

}

 开始时间是:2009-8-25 10:45
登录名为zhangSan 的用户 张三登录成功!
张三在打怪!
张三 又升了一级!
结束时间是:2009-8-26 03:40

不错!自己没有动手游戏就升级了。

 强制代理

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

 玩家接口

public interface IGamePlayer {
	// 登录游戏
	public void login(String user, String password);

	// 杀怪,这是网络游戏的主要特色
	public void killBoss();

	// 升级
	public void upgrade();
	
	//返回代理
	public IGamePlayer getProxy();
}

 玩家实体,必须有一个代理者代理自己。

public class GamePlayer implements IGamePlayer {

	private String name = "";
	// 我的代理是谁
	private GamePlayerProxy proxy = null;

	// 通过构造函数传递名称
	public GamePlayer(String _name) {
		this.name = _name;
	}

	// 打怪,最期望的就是杀老怪
	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("请使用指定的代理访问");
		}
	}

	/// 找到我的代理
	public IGamePlayer getProxy() {
		this.proxy = new GamePlayerProxy(this);
		return  this.proxy;
	}

	// 校验是否是代理访问
	private boolean isProxy() {
		if (this.proxy == null) {
			return false;
		} else {
			return true;
		}
	}

}

 代理者

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;
	}

}

 最后我们看看结果

public class Client {
	public static void main(String[] args) {
		// 定义一个游戏的角色
		IGamePlayer player = new GamePlayer("张三");
		// 获得指定的代理
		IGamePlayer proxy = player.getProxy();
		// 开始打游戏,记下时间戳
		System.out.println("开始时间是:2009-8-25 10:45");
		proxy.login("zhangSan", "password");
		// 开始杀怪
		proxy.killBoss();
		// 升级
		proxy.upgrade();
		// 记录结束游戏时间
		System.out.println("结束时间是:2009-8-26 03:40");
	}
}

开始时间是:2009-8-25 10:45
登录名为zhangSan的用户张三登录成功!
张三在打怪!
张三 又升了一级!
结束时间是:2009-8-26 03:40

自己没有动手游戏又升了一级 

动态代理

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

 在类图中增加了一个InvocationHandler接口和GamePlayIH类,作用就是产生一个对象的代理对象,其中InvocationHandler是JDK提供的动态代理接口,对被代理类的方法进行代理。

动态代理类

public class MyInvocationHandler implements InvocationHandler {
	// 被代理的对象
	private Object target = null;

	// 通过构造函数传递一个对象
	public MyInvocationHandler(Object _obj) {
		this.target = _obj;
	}

	// 代理方法
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// 设置返回值
		Object result = null;
		// 前置通知
		this.before();
		// 执行被代理的方法
		result = method.invoke(this.target, args);
		// 后置通知
		this.after();

		// 返回值
		return result;
	}

	// 前置通知
	public void before() {
		System.out.println("执行before方法");
	}

	// 后置通知
	public void after() {
		System.out.println("执行after方法");
	}

}

 玩家接口和玩家实体不变,动态代理类

public class DynamicProxy<T> {

	public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
		T newProxyInstance = (T) Proxy.newProxyInstance(loader, interfaces, h);
		return newProxyInstance;
	}
}

最后看看结果

public class Client {

	public static void main(String[] args) throws Throwable {
		// 定义一个痴迷的玩家
		IGamePlayer player = new GamePlayer("张三");

		// 开始打游戏,记下时间戳
		System.out.println("开始时间是:2009-8-25 10:45");

		// 获得类的class loader
		ClassLoader cl = player.getClass().getClassLoader();

		IGamePlayer proxy = DynamicProxy.newProxyInstance(cl, new Class[] { IGamePlayer.class },
				new MyIvocationHandler(player));

		// 登陆
		proxy.login("zhangSan", "password");
		// 开始杀怪
		proxy.killBoss();
		// 升级
		proxy.upgrade();

		// 记录结束游戏时间
		System.out.println("结束时间是:2009-8-26 03:40");

	}

}

开始时间是:2009-8-25 10:45
登录名为zhangSan 的用户 张三登录成功!
有人在用我的账号登陆!
张三在打怪!
张三 又升了一级!
结束时间是:2009-8-26 03:40 

代理模式还是好,自己躺着就把工作做了。 

 模版代码

1.ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。

public interface Subject {

	// 定义一个方法
	public void request();
}

2.RealSubject:真实主题角色,是实现抽象主题接口的类。

public class RealSubject implements Subject {

	// 实现方法
	public void request() {
		// 业务逻辑处理
	}

}

3.Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。

public class Proxy implements Subject {
	// 要代理哪个实现类
	private Subject subject = null;

	// 默认被代理者
	public Proxy() {
		this.subject = new Proxy();
	}

	public Proxy(Subject _subject) {
		this.subject = _subject;
	}

	// 通过构造函数传递代理者
	public Proxy(Object... objects) {

	}

	// 实现接口中定义的方法
	public void request() {
		this.before();
		this.subject.request();
		this.after();
	}

	// 预处理
	private void before() {
		// do something
	}

	// 善后处理
	private void after() {
		// do something
	}
}

小结

动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样对每一个方法或方法组合进行处理。Proxy 很美很强大,但是仅支持 interface 代理。Java 的单继承机制注定了这些动态代理类们无法实现对 class 的动态代理。好在有cglib为Proxy提供了弥补。class与interface的区别本来就模糊,在java8中更是增加了一些新特性,使得interface越来越接近class,当有一日,java突破了单继承的限制,动态代理将会更加强大。

猜你喜欢

转载自blog.csdn.net/weixin_40657079/article/details/81843026