zoukankan      html  css  js  c++  java
  • 代理模式

    杀鸡不想用牛刀-用代理

      大家好,我是小赵,求职的路虽然难,但最终还是有个着落,我现在进了藏剑山庄任职铸剑师,不过没意思,因为活都是低级的活,批量铸些普通的匕首、短剑之类,一天到晚忙个没完,这藏剑山庄果然是个大厂,订单超级多。

      做着做着我就没动力了,没啥技术含量,虽然是计件,但还不如我的打印机业务赚钱来的多,于是我就私底下请一些有空的同事帮我干活,而我就每天打个卡,然后就在家发展我的打印事业。

      其实主要原因呢,还是因为我暂时不打算离职,先静观其变,等待机会,毕竟这是个大企业。

      现在,是写日记的时候,我要记录一下我请同事干活的过程。

      首先,同事给我干活,那就必须要有和我一样的技能,比如调剂、熔炼、浇铸、加工这些铸剑流程,所以铸剑的流程应该抽象出来。而同事给我干活,天知地知,虽然是他干的活,但这些剑的创建者名字必须是我。

      思考完毕之后,我画下了类图:

      接下来我就根据这个类图来写程序

    铸剑流程抽象:

    public interface IMakeSword {
        //调剂
        void adjust();
    
        //熔炼
        void smelt();
    
        //浇铸
        void casting();
    
        //加工
        void process();
    } 

    我:

    public class User implements IMakeSword {
        //名字
        private String name = "";
    
        public User(String name) {
            this.name = name;
        }
        @Override
        public void adjust() {
            System.out.println(this.name + "正在调剂...");
        }
        @Override
        public void smelt() {
            System.out.println(this.name + "正在熔炼...");
        }
        @Override
        public void casting() {
            System.out.println(this.name + "正在浇铸...");
        }
        @Override
        public void process() {
            System.out.println(this.name + "正在加工...");
        }
    } 

    同事:

    public class Colleague implements IMakeSword{
        private IMakeSword user;
    
        //构造函数把我传进来
        public Colleague(IMakeSword user) {
            this.user = user;
        }
        @Override
        public void adjust() {
            this.user.adjust();
        }
        @Override
        public void smelt() {
            this.user.smelt();
        }
        @Override
        public void casting() {
            this.user.casting();
        }
        @Override
        public void process() {
            this.user.process();
        }
    }

      差不多了,现在我的同事要开始给我干活了:

        public static void main(String[] args) {
            IMakeSword xiaoZhao = new User("小赵");
            IMakeSword colleague = new Colleague(xiaoZhao);
            colleague.adjust();
            colleague.smelt();
            colleague.casting();
            colleague.process();
        }

    输出:

    小赵正在调剂...
    小赵正在熔炼...
    小赵正在浇铸...
    小赵正在加工...

      这个活干的是非常的好,工资我当然会转给同事,但是我的工作量在山庄里还不至于难看。

      随手一招代理模式,暂时保着这份工作,赚个五险一金。

    严查之下-强制代理

      后来,不知道是哪里传出的风声,山庄高层领导好像知道了什么,安排了专案小组暗地里对一些不正之风进行严查严打。

      我不怕顶风作案,就怕做的不严实。

      如果,专案小组假扮我的同事,那我不就嗝屁了吗?于是,我决定把我和我同事的关系隐藏到内部,不让人看到,也不接受外面传入。

      来看看我更改后的程序:

    铸剑流程抽象:

    public interface IMakeSword {
        //调剂
        void adjust();
    
        //熔炼
        void smelt();
    
        //浇铸
        void casting();
    
        //加工
        void process();
    
        //获取我的代理
        IMakeSword getProxy();
    }

      加一个getProxy方法,获取我的代理,就是我的同事嘛,增加这个方法是为了防止外人假冒。

    我:

    public class User implements IMakeSword {
        //名字
        private String name = "";
    
        //我的同事
        private IMakeSword proxy = null;
    
        public User(String name) {
            this.name = name;
        }
        @Override
        public void adjust() {
            if(null == this.proxy){
                System.out.println("你好,"+this.name+"在休息。");
                return;
            }
            System.out.println(this.name + "正在调剂...");
        }
        @Override
        public void smelt() {
            if(null == this.proxy){
                System.out.println("你好,"+this.name+"在休息。");
                return;
            }
            System.out.println(this.name + "正在熔炼...");
        }
        @Override
        public void casting() {
            if(null == this.proxy){
                System.out.println("你好,"+this.name+"在休息。");
                return;
            }
            System.out.println(this.name + "正在浇铸...");
        }
        @Override
        public void process() {
            if(null == this.proxy){
                System.out.println("你好,"+this.name+"在休息。");
                return;
            }
            System.out.println(this.name + "正在加工...");
        }
    
        @Override
        public IMakeSword getProxy() {
            //我和同事的小黑屋
            this.proxy = new Colleague(this);
            return this.proxy;
        }
    }

    在getProxy方法里,我在小黑屋里自己联系我的同事,并且在每个动作执行的时候,先检测我有没有代理,没代理的话千万别乱动。

    同事:

    public class Colleague implements IMakeSword{
        private IMakeSword user;
    
        //构造函数把我传进来
        public Colleague(IMakeSword user) {
            this.user = user;
        }
        @Override
        public void adjust() {
            this.user.adjust();
        }
        @Override
        public void smelt() {
            this.user.smelt();
        }
        @Override
        public void casting() {
            this.user.casting();
        }
        @Override
        public void process() {
            this.user.process();
        }
    
        @Override
        public IMakeSword getProxy() {
            return this;
        }
    }

    同事类则几乎没变化,getProxy方法也不需要的,返回同事自己就可以了。

      来,现在再试一下合作:

        public static void main(String[] args) {
            IMakeSword xiaoZhao = new User("小赵");
            IMakeSword colleague = xiaoZhao.getProxy();
            colleague.adjust();
            colleague.smelt();
            colleague.casting();
            colleague.process();
        }

    输出:

    小赵正在调剂...
    小赵正在熔炼...
    小赵正在浇铸...
    小赵正在加工...

      这是新的合作方式,谁也不知道我的代理是谁。

    如果专案小组来假冒同事呢:

        public static void main(String[] args) {
            IMakeSword xiaoZhao = new User("小赵");
            IMakeSword colleague = new Colleague(xiaoZhao);
            colleague.adjust();
            colleague.smelt();
            colleague.casting();
            colleague.process();
        }

    输出:

    你好,小赵在休息。
    你好,小赵在休息。
    你好,小赵在休息。
    你好,小赵在休息。

      这就是强制代理的手法,代理由自己管理,从此以后,我又可以高枕无忧了。

    降低合作门槛-动态代理

      我没想到,我那个同事不仅接我的活,还同时接别人的活,可别人就没我这么聪明啊,天天对着他new、new、new!结果没过几天就被专案小组给逮着了,直接翻船,同事也被供出来了,被劝退。

      

      随着我同事的牺牲,山庄的严打行动逐渐结束,不久后专案小组解散,紧张的氛围舒缓而开。

      但是我得从新找合作人啊,私底下接触了几个有意向的同事,都觉得我的合作方式太过麻烦,必须要有代理类,就是Colleague类,要实现一箩筐方法,而且创建了类就是留下了证据,将来就有被查到的风险。

      

      在我的一阵子研究之后,发现JDK有一个InvocationHandler接口可以用,于是乎,我重新写了一个程序。

    铸剑流程抽象:

    public interface IMakeSword {
        //调剂
        void adjust();
    
        //熔炼
        void smelt();
    
        //浇铸
        void casting();
    
        //加工
        void process();
    }

    我:

    public class User implements IMakeSword {
        //名字
        private String name = "";
    
        public User(String name) {
            this.name = name;
        }
        @Override
        public void adjust() {
            System.out.println(this.name + "正在调剂...");
        }
        @Override
        public void smelt() {
            System.out.println(this.name + "正在熔炼...");
        }
        @Override
        public void casting() {
            System.out.println(this.name + "正在浇铸...");
        }
        @Override
        public void process() {
            System.out.println(this.name + "正在加工...");
        }
    }

    动态代理类:

    public class ColleagueHandler implements InvocationHandler {
        Object object = null;
    
        //代理的目标
        public ColleagueHandler(Object object) {
            this.object = object;
        }
    
        //调用目标方法
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(this.object,args);
        }
    }

    开始干活:

        public static void main(String[] args) throws Throwable{
            IMakeSword xiaoZhao = new User("小赵");
            InvocationHandler handler = new ColleagueHandler(xiaoZhao);
            handler.invoke(null,xiaoZhao.getClass().getMethod("adjust", null),null);
            handler.invoke(null,xiaoZhao.getClass().getMethod("smelt", null),null);
            handler.invoke(null,xiaoZhao.getClass().getMethod("casting", null),null);
            handler.invoke(null,xiaoZhao.getClass().getMethod("process", null),null);
        }

    输出:

    小赵正在调剂...
    小赵正在熔炼...
    小赵正在浇铸...
    小赵正在加工...

      很好,程序实现了,从始至终都没有创建代理类,虽然很多人都会说这是动态代理,但实际上却不是,这只是反射而已。

      对于一个优秀的架构师来讲,这简直就是渣渣玩法,一箩筐方法名都写死在外面了,去他大爷的JDK。

      在寻求优化的过程中,听说JDK还提供了Proxy类,里面有个newProxyInstance方法用来创建一个对象的代理对象,这个方法总共有3个参数,ClassLoader loader用来指明生成代理对象使用哪个类装载器,Class<?>[] interfaces用来指明生成哪个对象的代理对象,通过接口指定,InvocationHandler h用来指明产生的这个代理对象要做什么事情。

      于是乎,我又舔着大逼脸去使用JDK的接口了。

    增加一个动态代理类:

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

    开始干活:

        public static void main(String[] args) throws Throwable{
            IMakeSword xiaoZhao = new User("小赵");
            ClassLoader cl = xiaoZhao.getClass().getClassLoader();
            IMakeSword proxy = MyProxy.newProxyInstance(cl,new Class[] {IMakeSword.class}, new ColleagueHandler(xiaoZhao));
            proxy.adjust();
            proxy.smelt();
            proxy.casting();
            proxy.process();
            System.out.println(xiaoZhao.equals(proxy));
        }

    输出:

    小赵正在调剂...
    小赵正在熔炼...
    小赵正在浇铸...
    小赵正在加工...
    false

      最后一个比较两个对象是否相同,证明了proxy对象并不是我,但实际上却又是我在干活,这才叫动态代理。

    通知机制-切面

      其实我们细看之下,已经发现了一个情况,就是铸剑相关的类已经和代理解耦了。

      第一条线:IMakeSword接口->User类。

      第二条线:InvocationHandler接口->ColleagueHandler类->MyProxy类。

      动态代理实现代理的职责,业务逻辑负责功能实现。

      现在,为了安全起见,我希望做一件事情,就是每次干活的时候,要给我发一个通知,让我知道。

      其实非常的简单,我们在创建动态代理的时候发这个通知就行了。

      先创建一个通知抽象,因为以后可能会有前置通知、后置通知、各种切入的通知等等,所以通知也是一条独立发展的线。

    通知抽象:

    public interface IAdvice {
        void exec();
    }

    前置通知:

    public class BeforeAdvice implements IAdvice {
        @Override
        public void exec() {
            System.out.println("准备要干活了");
        }
    }

    动态代理类:

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

    开始干活:

    public static void main(String[] args) throws Throwable{
            IMakeSword xiaoZhao = new User("小赵");
            ClassLoader cl = xiaoZhao.getClass().getClassLoader();
            IMakeSword proxy = MyProxy.newProxyInstance(cl,new Class[] {IMakeSword.class}, new ColleagueHandler(xiaoZhao));
            proxy.adjust();
            proxy.smelt();
            proxy.casting();
            proxy.process();
            System.out.println(xiaoZhao.equals(proxy));
        }

    输出:

    准备要干活了
    小赵正在调剂...
    小赵正在熔炼...
    小赵正在浇铸...
    小赵正在加工...
    false

      如果有的代理需要通知,有的时候不需要通知呢?很简单,传个参数,写个if条件即可,如果不想传参数呢?一般AOP的做法是使用注解的方式标记切入位置,然后在动态代理类里面对注解进行判断。

      获取注解也可以使用反射机制实现,类头上的注解、方法头上的注解、参数上的注解都可以获取,有兴趣的朋友可以自行研究。

    最后声明

    本故事写到后面有点怪,原因是动态代理和静态代理的使用场景是不同的,用同一个故事延续下来导致需求逻辑上出现了一些bug,在此致歉。

  • 相关阅读:
    POJ3258River Hopscotch(二分)
    POJ3273Monthly Expense(二分)
    POJ1002487-3279(map)
    HDU 3123 GCC
    POJ2031Building a Space Station
    POJ3096Surprising Strings(map)
    模板类
    POJ1265Area
    【ZZ】C 语言中的指针和内存泄漏 & 编写高效的C程序与C代码优化
    学习笔记之MySQL
  • 原文地址:https://www.cnblogs.com/fengyumeng/p/10750894.html
Copyright © 2011-2022 走看看