《Android源码设计模式》之代理模式

代理模式介绍

代理模式(Proxy Pattern)也称为委托模式,是结构型模式的一种。

代理模式的定义

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

代理模式的使用场景

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

代理模式的UML类图

在这里插入图片描述
根据类图可以得出如下一个代理模式的通用代码:

抽象主题类
package com.guifa.patterndemo.proxypattern;

public abstract class Subject {

    /**
     * 一个普通的业务方法
     */
    public abstract void visit();
}
实现抽象主题的真实主题类
package com.guifa.patterndemo.proxypattern;

public class RealSubject extends Subject {
    @Override
    public void visit() {
        // RealSubject中visit的具体逻辑
        System.out.println("Real Subject");
    }
}
代理类
package com.guifa.patterndemo.proxypattern;

public class ProxySubject extends Subject {

    private RealSubject realSubject;

    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void visit() {
        // 通过真实主题引用的对象调用真实主题中的逻辑方法
        realSubject.visit();
    }
}
客户类
package com.guifa.patterndemo.proxypattern;

public class Client {
    public static void main(String[] args) {
        // 构造一个真实的主题对象
        RealSubject realSubject = new RealSubject();
        // 通过真实的主题对象构造一个代理对象
        ProxySubject proxySubject = new ProxySubject(realSubject);
        // 调用代理的相关方法
        proxySubject.visit();
    }
}

角色介绍:
Subject:抽象主题类,该类的主要职责是声明真实主题与代理的共同接口方法,该类既可以是一个抽象类,也可以是一个接口。
RealSubject:真实主题类,该类也称为委托类或被代理类,该类定义了代理所表示的真实对象,由其执行具体的业务逻辑方法,而客户类则通过代理类间接地调用真实主题类中定义的方法。
ProxySubject:代理类,该类也称为委托类或代理类,该类持有一个对真实主题类的引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行,以此起到代理的作用。
Client:客户类,即使用代理类的类型。

代理模式的简单实现

我们以找律师作为我没诉讼的代理人为例,将诉讼的流程抽象在一个接口类中。代码如下:

诉讼接口类
package com.guifa.patterndemo.proxypattern;

public interface ILawsuit {

    // 提交申请
    void submit();

    // 进行举证
    void burden();

    // 开始辩护
    void defend();

    // 诉讼完成
    void finish();
}

4个方法非常简单,都是诉讼的一般流程。

具体诉讼人
package com.guifa.patterndemo.proxypattern;

public class XiaoMin implements ILawsuit {
    @Override
    public void submit() {
        // 老板拖欠工资,小民只好申请仲裁
        System.out.println("老板拖欠工资!特此申请仲裁!");
    }

    @Override
    public void burden() {
        // 小民证据充足,不怕告不赢
        System.out.println("这是合同书和过去一年的银行工资流水");
    }

    @Override
    public void defend() {
        // 铁证如山,辩护也没什么好说的
        System.out.println("证据确凿!不需要再说什么了!");
    }

    @Override
    public void finish() {
        // 结果也是肯定的,必赢
        System.out.println("诉讼成功!判决老板即日起七天内结算工资!");
    }
}

如上所述,该类实现ILawsuit并对其中4个方法做出具体的实现逻辑,逻辑很简单,都只是输出一段话而已,当然,小民自己是不会去打官司的,于是小民请个律师代替自己进行诉讼。

代理律师
package com.guifa.patterndemo.proxypattern;

public class Lawyer implements ILawsuit {

    private ILawsuit iLawsuit;

    public Lawyer(ILawsuit iLawsuit) {
        this.iLawsuit = iLawsuit;
    }

    @Override
    public void submit() {
        iLawsuit.submit();
    }

    @Override
    public void burden() {
        iLawsuit.burden();
    }

    @Override
    public void defend() {
        iLawsuit.defend();
    }

    @Override
    public void finish() {
        iLawsuit.finish();
    }
}

律师类表示代理者律师,在该类里面会持有一个被代理者的引用,律师所执行的方法实质就是简单地调用被代理者中的方法,下面来看看客户类中具体的调用执行关系。

客户类
package com.guifa.patterndemo.proxypattern;

public class Client {
    public static void main(String[] args) {
        // 构造一个小民
        XiaoMin xiaoMin = new XiaoMin();
        // 构造一个代理律师并将小民作为构造参数传递进去
        Lawyer lawyer = new Lawyer(xiaoMin);
        // 律师进行诉讼申请
        lawyer.submit();
        // 律师进行举证
        lawyer.burden();
        // 律师代替小民进行辩护
        lawyer.defend();
        // 完成诉讼
        lawyer.finish();
    }
}

输出结果:
在这里插入图片描述
代理模式可以大致分为两大部分,一是静态代理,二是动态代理。静态代理如上述示例哪有,代理者的代码由程序员自己或通过一些自动化工具生成固定的代码再对其进行编译,也就是说在我们的代码运行前代理类的class编译文件就已存在;而动态代理则与静态代理相反,通过反射机制动态地生成代理者对象,也就是说我们在code阶段压根就不需要知道代理谁,代理谁我们将会在执行阶段决定,而Java也给我们提供了一个便捷的动态代理接口InvocationHandler,实现该接口需要重写其调用方法invoke。

package com.guifa.patterndemo.proxypattern;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicProxy implements InvocationHandler {

    private Object object;

    public DynamicProxy(Object object) {
        this.object = object;
    }

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

如上代码所述,我们声明一个Object的引用,该引用将指向被代理类,而我们调用被代理类的具体方法则在invoke方法中执行,也就是说我没原来由代理类所做的工作,现在有InvocationHandler来处理,不再需要关系到底代理谁,下面我们修改客户类的逻辑。

package com.guifa.patterndemo.proxypattern;

import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        // 构造一个小民
        XiaoMin xiaoMin = new XiaoMin();
        // 构造一个动态代理
        DynamicProxy dynamicProxy = new DynamicProxy(xiaoMin);
        // 获取被代理类小民的ClassLoader
        ClassLoader classLoader = xiaoMin.getClass().getClassLoader();
        // 动态构造一个代理者律师
        ILawsuit lawyer = (ILawsuit) Proxy.newProxyInstance(classLoader, new Class[]{ILawsuit.class}, dynamicProxy);
        // 律师进行诉讼申请
        lawyer.submit();
        // 律师进行举证
        lawyer.burden();
        // 律师代替小民进行辩护
        lawyer.defend();
        // 完成诉讼
        lawyer.finish();
    }
}

静态代理和动态代理是从code方面来区分代理模式的两种方式,我没也可以从其适用范围来区分不同类型的代理实现。

  • 远程代理(Remote Proxy):为某个对象在不同的内存地址空间提供局部代理。使系统可以将Service部分的实现隐藏,以便Client可以不必考虑Service的存在。
  • 虚拟代理(Virtual Proxy):使用一个代理对象表示一个十分耗资源的对象并在真正需要时才创建。
  • 保护代理(Protection Proxy):使用代理控制对原始对象的访问。该类型的代理常被用于原始对象有不同访问权限的情况。
  • 智能代理(Smart Reference):在访问院士对象时执行一些自己附加操作并对指向原始对象的引用计数。
  • 这里要注意的是,静态和动态代理都可以应用于上述4种情形,两者是各自独立的变化。
总结

代理模式应用广泛,而且代理模式几乎没有什么缺点可言,它是细分化至很小的一种模式,要真的说一个缺点,那么就是所有设计模式的通病:对类的增加。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值