【设计模式】代理模式(Proxy Pattern)

学而不思则罔,思而不学则殆


我发现一个很常见的现象,很多文章都在讲设计模式,代理模式,但是呢没有举出一些项目或者实际工作用到的例子,我感觉这样不能加深我们的理解,感觉只是一个知识点,而没有实际的运用,所以本篇文章根据个人理解,总结了三个常用的代理模式,有理解不对的地方,欢迎指出,共同进步。

代理模式

Proxy Pattern 代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

常见代理模式

静态代理

静态代理类图

在这里插入图片描述

Demo

    interface Subject {
        void request();
    }

    static class RealSubject implements Subject {

        @Override
        public void request() {
            System.out.println("RealSubject request");
        }
    }

    static class Proxy implements Subject {
        Subject subject;

        @Override
        public void request() {
            if (subject == null) {
                subject = new RealSubject();
            }
            subject.request();
        }
    }

    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        proxy.request();
    }

动态代理

动态代理Demo

   interface Subject {
        void request();

        int add(int a, int b);
    }

    static class RealSubject implements Subject {

        @Override
        public void request() {
            System.out.println("RealSubject request");
        }

        @Override
        public int add(int a, int b) {
            return a + b;
        }
    }
    //测试动态代理
    private static void test1() {
        Subject proxyInstance = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new
                Class[]{Subject.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("动态代理 前 proxy:" + proxy.getClass() + " method:" + method + " args:" + Arrays.toString(args));
                RealSubject realSubject = new RealSubject();
                Object result = method.invoke(realSubject, args);
                System.out.println("动态代理 后");
                return result;
            }
        });

        System.out.println("proxyInstance:" + proxyInstance);
        System.out.println("proxyInstance.toString:" + proxyInstance.toString());
        System.out.println("proxyInstance.hashCode:" + proxyInstance.hashCode());
        proxyInstance.request();
        System.out.println("add(2,3):" + proxyInstance.add(2, 3));
    }

动态代理有三个参数【Proxy.newProxyInstance()】:

  1. ClassLoader loader 指定当前目标对象使用的类加载器,获取加载器的方法是固定的
  2. Class<?>[] interfaces 指定目标对象实现的接口的类型,使用泛型方式确认类型
  3. InvocationHandler h 指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法

测试结果如下:


动态代理 前 proxy:class com.pattern.$Proxy0 method:public java.lang.String java.lang.Object.toString() args:null
动态代理 后
proxyInstance:com.pattern.ProxyPattern$RealSubject@5e2de80c
动态代理 前 proxy:class com.pattern.$Proxy0 method:public java.lang.String java.lang.Object.toString() args:null
动态代理 后
proxyInstance.toString:com.pattern.ProxyPattern$RealSubject@1d44bcfa
动态代理 前 proxy:class com.pattern.$Proxy0 method:public native int java.lang.Object.hashCode() args:null
动态代理 后
proxyInstance.hashCode:644117698
动态代理 前 proxy:class com.pattern.$Proxy0 method:public abstract void com.pattern.ProxyPattern$Subject.request() args:null
RealSubject request
动态代理 后
动态代理 前 proxy:class com.pattern.$Proxy0 method:public abstract int com.pattern.ProxyPattern$Subject.add(int,int) args:[2, 3]
动态代理 后
add(2,3):5

我发现不止调用接口的方法request和add方法,就算调用常规的toString和hashCode方法也会调用invoke方法,走动态代理的逻辑。

代理模式应用

网络代理

Charles网络抓包工具,当我们使用本地代理或者远程代理的时候,就是一种代理模式。我们只管从Charles中获取数据,至于Charlei内部数据从本地还是从网络我都不关心

Android的Context

Context源码如下:查看其中重要的部分。

public class ContextWrapper extends Context {
    @UnsupportedAppUsage
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
    ...
    public Context getBaseContext() {
        return mBase;
    }

    @Override
    public AssetManager getAssets() {
        return mBase.getAssets();
    }

    @Override
    public Resources getResources() {
        return mBase.getResources();
    }

    @Override
    public PackageManager getPackageManager() {
        return mBase.getPackageManager();
    }

    @Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }

    @Override
    public Looper getMainLooper() {
        return mBase.getMainLooper();
    }

    @Override
    public Executor getMainExecutor() {
        return mBase.getMainExecutor();
    }

    @Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }
    ...
}

Context类图

在这里插入图片描述

Java中的ReentrantLock

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;
    ...
    public void lock() {
        sync.lock();
    }

    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    public void unlock() {
        sync.release(1);
    }

    public Condition newCondition() {
        return sync.newCondition();
    }

    public int getHoldCount() {
        return sync.getHoldCount();
    }

    public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }

    public boolean isLocked() {
        return sync.isLocked();
    }

    public final boolean isFair() {
        return sync instanceof FairSync;
    }

    protected Thread getOwner() {
        return sync.getOwner();
    }

    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }

    public final boolean hasQueuedThread(Thread thread) {
        return sync.isQueued(thread);
    }

    public final int getQueueLength() {
        return sync.getQueueLength();
    }

    protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }
    ...
}

查看ReentrantLock 的源码,保留其中的重要方法,我的理解这是一种很直接的代理模式的应用,所有的工作都是sync对象在实现和工作的,而ReentrantLock只是sync的一个代表。

RxJava中的Observer(订阅者)接口

RxJava的几十上百个操作符内部都是采用代理模式(个人理解)。当我们建立订阅的时候,每一个Observable内部都会新建一个新的Observer,新建的Observer持有我们建立订阅关系时传入的Observer对象,然后在用内部新建的Observer对象去订阅上一级的Observable,接收上一级下发的消息,依次类推。

随便举一个例子:

        static final class InnerObserver<U> extends AtomicReference<Disposable> implements Observer<U> {

            private static final long serialVersionUID = -7449079488798789337L;

            final Observer<? super U> downstream;
            final SourceObserver<?, ?> parent;

            InnerObserver(Observer<? super U> actual, SourceObserver<?, ?> parent) {
                this.downstream = actual;
                this.parent = parent;
            }

            @Override
            public void onSubscribe(Disposable d) {
                DisposableHelper.replace(this, d);
            }

            @Override
            public void onNext(U t) {
                downstream.onNext(t);
            }

            @Override
            public void onError(Throwable t) {
                parent.dispose();
                downstream.onError(t);
            }

            @Override
            public void onComplete() {
                parent.innerComplete();
            }

            void dispose() {
                DisposableHelper.dispose(this);
            }
        }
    }

InnerObserver是内部downstream(真实观察者)的代表。

InnerObserver 类图

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值