代理模式的定义:
为其他对象提供一种代理以控制对这个对象的访问。
例子:玩家玩网游打怪、升级~
见UML图
代码
//玩家接口类
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;
}
@Override
public void login(String user, String password) {
System.out.println("登录名为" + user + "的用户" + this.name + "登陆成功");
}
@Override
public void killBoss() {
System.out.println(this.name + " 在打怪");
}
@Override
public void upgrade() {
System.out.println(this.name + " 又生了一级");
}
}
//场景类
public class Client {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("张三");
System.out.println("开始时间是2016-8-25 9:57");
player.login("张三", "password");
player.killBoss();
player.upgrade();
System.out.println("结束时间是:2016-8-25 9:58");
}
}
/*Output
开始时间是2016-8-25 9:57
登录名为张三的用户张三登陆成功
张三 在打怪
张三 又生了一级
结束时间是:2016-8-25 9:58
*/
玩过网游的人都知道,升级打怪很累的,一般的网游都是满级之后才开始真正入门(好像暴露了什么),所以就有了代打。我替你升级,你给我money
见UML图
//新增代理类
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(IGamePlayer _gamePlayer){
this.gamePlayer = _gamePlayer;
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user, password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
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("开始时间是:2016-8-25 10:14");
proxy.login("张三", "password");
proxy.killBoss();
proxy.upgrade();
System.out.println("结束时间是: 2016-8-25 10:15");
}
}
一个主题类和一个代理类,这就是最简洁的代理模式。
它虽然简单但好多设计模式的本质其实就是代理模式(我觉得它就跟继承差不多了,别的模式不涉及到它才怪~)
这里就不说代理模式的优点了~
代理模式的拓展
一、普通代理
要求:客户端(场景类只能访问代理角色,而不能访问真实角色)
见UMl图
//修改后的玩家类
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;
}
}
@Override
public void login(String user, String password) {
System.out.println("登录名为" + user + "的用户" + this.name + "登陆成功");
}
@Override
public void killBoss() {
System.out.println(this.name + " 在打怪");
}
@Override
public void upgrade() {
System.out.println(this.name + " 又生了一级");
}
}
//修改后的代理类
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer = null;
//通过构造函数传递参数确定对谁进行代练(传递 代理者名字就可以代理了)
public GamePlayerProxy(String name){
try{
gamePlayer = new GamePlayer(this, name);
}catch(Exception e){
e.printStackTrace();
}
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user, password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
}
}
//场景类
public class Client {
public static void main(String[] args) {
IGamePlayer proxy = new GamePlayerProxy("张三");
System.out.println("开始时间是:2016-8-25 10:14");
proxy.login("张三", "password");
proxy.killBoss();
proxy.upgrade();
System.out.println("结束时间是: 2016-8-25 10:15");
}
}
/*Output
开始时间是2016-8-25 9:57
登录名为张三的用户张三登陆成功
张三 在打怪
张三 又生了一级
结束时间是:2016-8-25 9:58
*/
运行结果完全相同。但是!场景类只知道代理而不知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的玩家(主题角色)想怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口对应的方法。
该模式非常适合对拓展性要求非常高的场合。当然,在实际项目中一般都是通过约定来禁止new一个真实的角色,这也是很好的方案。
二、强制代理(例子不太好)
要求:强制代理不是要求代理替自己干活,而是通过代理来访问我。然后我自己干活~
(其实这个例子的意思是:你去看一个明星,那必须通过他的经纪人。你通过这个经纪人找到这个明星后肯定是和明星交流,而不是和经纪人交流)
//修改后的玩家接口(增加了getProxy()接口)
public interface IGamePlayer {
public void login(String user, String password);
public void killBoss();
public void upgrade();
//每个人通过getProxy来找到自己的代理
public IGamePlayer getProxy();
}
//修改后的玩家类
public class GamePlayer implements IGamePlayer {
private String name = "";
//我的代理是谁
private IGamePlayer proxy = null;
public GamePlayer(String _name){
this.name = name;
}
//找到自己的代理
@Override
public IGamePlayer getProxy() {
// TODO Auto-generated method stub
this.proxy = new GamePlayerProxy(this.name);
return this.proxy;
}
@Override
public void login(String user, String password) {
if(this.isProxy()){
System.out.println("登录名为" + user + "的用户" + this.name + "登陆成功");
}else{
System.out.println("请使用指定代理访问");
}
}
@Override
public void killBoss() {
if(this.isProxy()){
System.out.println(this.name + " 在打怪");
}else{
System.out.println("请使用指定代理访问");
}
}
@Override
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;
}
}
//修改后的代理类
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer = null;
private String name;
//通过构造函数传递参数确定对谁进行代练
public GamePlayerProxy(String name){
this.name = name;
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user, password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
}
@Override
public IGamePlayer getProxy() {
// 代理的代理暂时还没有 就是自己
return this;
}
}
ok,现在有三个场景类
1、直接访问真实角色
public class Client {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("张三");
System.out.println("开始时间是:2016-8-25 10:14");
player.login("张三", "password");
player.killBoss();
player.upgrade();
System.out.println("结束时间是: 2016-8-25 10:15");
}
}
/*Output
开始时间是:2016-8-25 10:14
请使用指定代理访问
请使用指定代理访问
请使用指定代理访问
结束时间是: 2016-8-25 10:15
*/
2、直接访问代理类
public class Client {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("张三");
IGamePlayer proxy = new GamePlayerProxy(player, ((GamePlayer) player).getName());
System.out.println("开始时间是:2016-8-25 10:14");
proxy.login("张三", "password");
proxy.killBoss();
proxy.upgrade();
System.out.println("结束时间是: 2016-8-25 10:15");
}
}
/*Output
开始时间是:2016-8-25 10:14
请使用指定代理访问
请使用指定代理访问
请使用指定代理访问
结束时间是: 2016-8-25 10:15
*/
3、强制代理
public class Client {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("张三");
IGamePlayer proxy = player.getProxy();
System.out.println("开始时间是:2016-8-25 10:14");
proxy.login("张三", "password");
proxy.killBoss();
proxy.upgrade();
System.out.println("结束时间是: 2016-8-25 10:15");
}
}
/*Output
开始时间是:2016-8-25 10:14
登录名为张三的用户登陆成功
在打怪
又生了一级
结束时间是: 2016-8-25 10:15
*/
优点就是:不允许你直接访问真实角色
三、虚拟代理
这个其实就是在需要的时候初始化对象,避免被代理对象较多而引起的初始化缓慢而修改了一下代理类初始化的代码
public class Proxy implements Subject{
//要代理的实现类
private Subject subject;
//实现接口中定义的方法
public void requst(){
if(subject == null){
subject = new RealSubject();
}else{
subject.request();
}
}
}
四、动态代理
动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定带了哪一个。相对来说,自己写代理类的方式就是静态代理。现在有一个非常流行的名称叫做面向横切面编程,也就是AOP(Aspect Oriented Programming),其核心就是采用了动态代理机制。还是以打游戏为例
见UML图
这里的InvocationHandler是JDK提供的动态代理接口
见程序代码
//JDK的接口
package java.lang.reflect;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
//动态代理类
//动态代理就是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称"我已经实现该接口的所有方法了"。
public class GamePlayIH implements InvocationHandler {
//被代理者
Class cls = null;
//被代理的实例
Object obj = null;
public GamePlayIH(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
Object result = method.invoke(this.obj, args);
return result;
}
}
//最开始的玩家类
public class GamePlayer implements IGamePlayer {
private String name = "";
public GamePlayer(String _name){
this.name = _name;
}
@Override
public void login(String user, String password) {
System.out.println("登录名为" + user + "的用户" + this.name + "登陆成功");
}
@Override
public void killBoss() {
System.out.println(this.name + " 在打怪");
}
@Override
public void upgrade() {
System.out.println(this.name + " 又生了一级");
}
}
//场景类
public class Client {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("张三");
InvocationHandler handler = new GamePlayIH(player);
System.out.println("开始时间: 2016-8-28 10:12");
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("结束时间: 2016-8-28 10:14");
}
}
/*Output
开始时间: 2016-8-28 10:12
登录名为zhangsan的用户张三登陆成功
张三 在打怪
张三 又生了一级
结束时间: 2016-8-28 10:14
*/
没有创建代理类,也没有实现IGamePlayer接口,只需要一个GamePlayH类就可以代理任何需要被代理的类了,这就是动态代理。
AOP是如何依赖动态代理的内容见下一篇
PS:一边开会一边写这篇博客,会议室的空气真是异常浑浊啊,头痛头痛