代理模式
代理模式:也被称为委托模式,为其他对象提供一种代理以控制对这个对象的访问(Provide a surrogate or placeholder for another object to control access to it)。
代理模式的通用类图中包含三种角色,抽象主体角色(Subject)、具体主题角色(RealSubject)、代理主题角色(Proxy),其通用类图如下:
Subject抽象主体角色:可以使抽象类也可以是接口,是一个普通的业务类型定义。
RealSubject具体主题角色:也叫做被委托角色、被代理角色,是业务逻辑的具体执行者。
Proxy代理主题角色:也叫做委托类、代理类,它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完成前后做预处理和后处理工作
代理模式通用类图的代码如下:
public interface Subject{
public void request();}
public class RealSubject implements Subject{
@Override
public void request() {
// TODO Auto-generated method stub
//具体的业务逻辑处理
} }
public class Proxy implements Subject{
private Subject subject = null;
public Proxy(Subject subject){
this.subject = subject;
}
@Override
public void request() {
// TODO Auto-generated method stub
this.before();
subject.request();
this.after();
}
private void after() {
//预处理
}
private void before() {
//后处理
}}
代理模式的优点
职责清晰:真实角色就是实现实际的业务逻辑,不用关心其他非本职工作的事务,通过后期代理完成非本职工作。
高扩展性:具体主题角色可以随时发生变化,只要它实现了接口,而不用关心它如何变化,只要接口没变,代理类可以在完全不做修改的情况下使用。
智能化:通过动态代理的方式实现在编码阶段不需要知道代理的对象。
代理模式的具体应用
普通代理模式,在该模式下调用者只知道代理而不用知道真实角色,屏蔽了真实角色的变更对高层模块的影响,在实际项目中,一般都是通过约定来禁止new一个真实的角色。
普通代理模式的实例类图如下:
具体源码如下,通过GamePlayerProxy的构造函数中创建被代理者对象。
public interface IGamePlayer {
public void login(String userName, String passwd);
public void killBoss();
public void upgrade();}
public class GamePlayer implements IGamePlayer{
public GamePlayer(IGamePlayer _proxy) throws Exception{
if(_proxy == null) //检查代理类是否能创建真是角色对象
throw new Exception("该代理不成代理该类");
}
@Override
public void login(String userName, String passwd) {
// TODO Auto-generated method stub
System.out.println(userName + "登入游戏");
}
@Override
public void killBoss() {
// TODO Auto-generated method stub
System.out.print("在打怪");
}
@Override
public void upgrade() {
// TODO Auto-generated method stub
System.out.println("升级");
}}
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gameplayer = null;
public GamePlayerProxy(){
try {
this.gameplayer = new GamePlayer(this);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void login(String userName, String passwd) {
// TODO Auto-generated method stub
this.gameplayer.login(userName, passwd);
}
@Override
public void killBoss() {
// TODO Auto-generated method stub
this.gameplayer.killBoss();
}
@Override
public void upgrade() {
// TODO Auto-generated method stub
this.gameplayer.upgrade();
}
}
public class Client{
public static void main(String[] args){
IGamePlayer proxy = new GamePlayerProxy();
proxy.login("Danny", "passwd");
proxy.killBoss();
proxy.upgrade();
}}
强制代理模式,该模式是通过真实角色查找到代理模式,其通用类图比普通代理类图只在接口处增加了getProxy()方法,其实例类图如下:
具体源码如下,其中通过GamePlayer的getProxy()方法取得代理,并且在GamePlayer私有方法isProxy()中增加了判断是否是通过代理访问方法
public interface IGamePlayer {
public void login(String userName, String passwd);
public void killBoss();
public void upgrade();
public IGamePlayer getProxy();}
public class GamePlayer implements IGamePlayer{
private IGamePlayer proxy = null;
@Override
public void login(String userName, String passwd) {
// TODO Auto-generated method stub
if(this.isProxy())
System.out.println(userName + "登入游戏");
else
System.out.println("请通过代理访问游戏");
}
@Override
public void killBoss() {
// TODO Auto-generated method stub
if(this.isProxy())
System.out.print("在打怪");
else
System.out.println("请通过代理访问游戏");
}
@Override
public void upgrade() {
// TODO Auto-generated method stub
if(this.isProxy())
System.out.println("升级");
else
System.out.println("请通过代理访问游戏");
}
@Override
public IGamePlayer getProxy() {
// TODO Auto-generated method stub
if(this.proxy == null)
this.proxy = new GamePlayerProxy(this);
return this.proxy;
}
private boolean isProxy(){
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
for(int i = 0; i < stack.length; i++){
String name = stack[i].getClassName();
if(name.contains("Proxy") && this.proxy != null)
return true;
}
return false;
}}
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gameplayer = null;
protected GamePlayerProxy(IGamePlayer player){
this.gameplayer = player;
}
@Override
public void login(String userName, String passwd) {
// TODO Auto-generated method stub
this.gameplayer.login(userName, passwd);
}
@Override
public void killBoss() {
// TODO Auto-generated method stub
this.gameplayer.killBoss();
}
@Override
public void upgrade() {
// TODO Auto-generated method stub
this.gameplayer.upgrade();
}
@Override
public IGamePlayer getProxy() {
// TODO Auto-generated method stub
return this;
}}
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
IGamePlayer player = new GamePlayer();
IGamePlayer proxy = player.getProxy();
proxy.login("Danny", "passwd");
proxy.killBoss();
proxy.upgrade();
}}
代理类可以为真实角色预处理消息、过滤消息、转发消息、事后处理等功能。代理类不仅仅可以有自己的运算方法,通常情况下代理的职责并不单一,它可以组合其它的真实角色,也可是实现自己的职责,如计费等。如下是代理实现计费的通用类图:
动态代理:在实现阶段不用关心代理谁,而在运行阶段才指定代理哪个对象,现在的面向切面编程(Aspect Oriented Programming)就是采用了动态代理的机制。在该类图中增加了一个InvocationHandler接口和GamePlayer类,作用就是产生一个对象的代理对象,其中InvocationHandler是JDK提供的动态代理接口,对被代理的方法进行代理,其具体实例类图如下:
实例类图的源码如下:
public class GamePlayerIH implements InvocationHandler{
//被代理的实例
private Object obj = null;
public GamePlayerIH(Object obj){
this.obj = obj;
}
//返回一个代理对象
public Object bind(){
return Proxy.newProxyInstance(this.obj.getClass().getClassLoader(), this.obj.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) //调用被代理的方法
throws Throwable {
// TODO Auto-generated method stub
Object result = method.invoke(this.obj, args); if(method.getName().equalsIgnoreCase("login")){ System.out.println("代练登入游戏"); }
return result;
}}
public class Client{
/**
* @param args
*/
public static void main(String[] args) {
IGamePlayer player = new GamePlayer();
IGamePlayer proxy = (IGamePlayer)new GamePlayerIH(player).bind();
proxy.login("zhangsan", "passwd");
proxy.killBoss();
proxy.upgrade();
}}
动态代理的通用类图如下:
很简单的两条独立发展的路线,动态代理实现代理的职责,具体的业务逻辑RealSubject实现相关的逻辑功能,两者之间没有必然的耦合。通知从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。
如下是通用类图中的MyInvacationHandler、DynamicProxy和Client的代码:
public class MyInvocationHandler implements InvocationHandler{
//被代理的实例
private Object obj = null;
public MyInvocationHandler(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
Object result = method.invoke(this.obj, args);
return result;
}}
public class DynamicProxy
public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h){
if(true){
(new BeforeAdvice()).exec();
}
return (T) Proxy.newProxyInstance(loader, interfaces, h);
}}
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
IGamePlayer player = new GamePlayer();
MyInvocationHandler handler = new MyInvocationHandler(player);
IGamePlayer proxy = DynamicProxy.newProxyInstance(player.getClass().getClassLoader(), player.getClass().getInterfaces(), handler);
proxy.killBoss();
}}
动态代理调用过程示意图如下:
最佳实践
要实现动态代理的首要条件是:被代理类必须实现一个接口,当然也有很多技术如CGLIB可以再不实现接口的情况下实现动态代理的方式。

Comments | NOTHING