代理模式

与装饰者模式的区别

代理模式的定义

为其他对象提供一种代理以控制对这个对象的访问

代理模式的使用场景

当无法或不想直接访问某个对象或访问某个对象存在困难的时候可以通过一个代理对象来间接访问,为保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。

UML类图

代理模式

代理模式的简单实现

代理模式的例子在生活中可以找到很多,比如遇到老板克扣工资的情况,我们可以通过法律手段来解决,那么久需要请一名律师来作为自己的诉讼代理人,我们将诉讼的流程抽象在一个接口类中。

//诉讼接口类
public interface ILawsuit {
    void submit();//提交材料
    
    void burden();//诉讼
    
    void defend();//辩护
    
    void finish();//完成

}
//具体诉讼人
public class Hengly implements ILawsuit {

    public void submit() {
        System.out.println("老板克扣了我2个月工资,申请维护劳动者的利益");

    }

    public void burden() {
        System.out.println("我没有违反公司任何规定,没有理由克扣的工资,这是合同以及过去2个月的考勤记录");
    }

    public void defend() {
        System.out.println("证据确凿,不用多说什么");

    }

    public void finish() {
        System.out.println("诉讼成功,老板需要在7天内结算清工资,否则违反劳动法,追究刑事责任");
    }

}
//代理律师
public class Lawyer implements ILawsuit {
    private ILawsuit mLawsuit;//持有一个具体被代理者的引用
    
    public  Lawyer(ILawsuit  l){
        mLawsuit=l;
    }
    

    public void submit() {
        mLawsuit.submit();
    }

    public void burden() {
        mLawsuit.burden();
    }

    public void defend() {
        mLawsuit.defend();
    }

    public void finish() {
        mLawsuit.finish();
    }

}

律师类和具体的诉讼人都实现了相同的接口,律师所执行的方法就是简单调用被代理者中的方法。下面看看客户类中具体的调用执行关系。

public class Client {
    
    public static void main(String[] args) {
        ILawsuit hengly=new Hengly();
        Lawyer  lawyer=new Lawyer(hengly);
        
        lawyer.submit();
        lawyer.burden();
        lawyer.defend();
        lawyer.finish();
    }

}

运行结果很简单就不用给出了,可以看到其实代理模式很简单,其主要还是一种委托的机制,真实对象将方法的执行委托给代理对象,这其实也就是静态代理:代理者的代码有程序员自己或一些自动化的工具生成固定的代码再进行编译,也就是说在我们代码运行之前代理类的class编译文件就已经存在。而动态代理与静态代理不同,通过反射机制动态的生成代理者的对象,也就是说我们在code阶段压根就不需要知道代理谁,代理谁我们会在执行阶段决定。而Java也给我们提供了一个便捷的动态代理接口InvocationHandler,实现该接口需要重写其调用方法invoke.

public class DynamicProxy implements InvocationHandler {
    private Object tag;//持有被代理类的引用
    
    public DynamicProxy(Object obj){
        tag=obj;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        // 调用被代理类对象的方法
        Object result=method.invoke(tag, args);
        return result;
    }

}

如上代码所诉我们声明了一个Object的引用,该引用指向被代理类,而我们调用被代理类的具体方法则在invoke方法中执行。

public class Client {
    
    public static void main(String[] args) {
        ILawsuit hengly=new Hengly();
        
        DynamicProxy  proxy=new DynamicProxy(hengly);
        
        ClassLoader   loader=hengly.getClass().getClassLoader();
        
        ILawsuit  lawyer=(ILawsuit)Proxy.newProxyInstance(loader, new Class[]{ILawsuit.class}, proxy);
        
        lawyer.submit();
        lawyer.burden();
        lawyer.defend();
        lawyer.finish();
    }

}

运行结果:

老板克扣了我2个月工资,申请维护劳动者的利益
我没有违反公司任何规定,没有理由克扣的工资,这是合同以及过去2个月的考勤记录
证据确凿,不用多说什么
诉讼成功,老板需要在7天内结算清工资,否则违反劳动法,追究刑事责任

代理模式可以大致分为两大部分,一是静态代理,而是动态代理。静态代理如上述示例那样,代理的代码由程序员自己或者通过一些自动化的工具生成的固定代码再对其进行编译;而动态代理则和静态代理相反,通过反射机制动态的生成代理者的对象。而Java也给我们提供了一个便捷的动态代理接口InvocatiionHandler,实现该接口需要重写其调用方法invoke.

Retrofit源码中的动态代理

我们发现使用retrofit需要去定义一个接口,然后通过调用retrofit.create(XXX.class)方法,得到一个接口的实例,最后通过这个实例去实现我们的操作。

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, Object... args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

到这里,你应该明白retrofit为我们接口生成实例对象并不神奇,仅仅是使用了Proxy这个类的API而已,然后在invoke方法里面拿到足够的信息去构建最终返回的Call而已。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容