代理模式


代理模式

代理模式:也被称为委托模式,为其他对象提供一种代理以控制对这个对象的访问(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可以再不实现接口的情况下实现动态代理的方式。

声明:DungCry.|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 代理模式


以此热爱,以此谋生。
Le vent se lève, il faut tenter de vivre