0%

设计模式之代理模式

代理模式(Proxy Pattern)也称为委托模式,是结构性模式的一种,它在日常开发中非常常用,实际上在日常生活中也非常常见,对于程序员来讲代理上网,叫同事帮忙买快餐也是代理模式,请律师处理纠纷也是代理模式,总之,它无处不在。

一、代理的定义

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

二、代理模式的使用场景

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

代理模式的UML图如下所示:


代理模式的通用代码:

抽象主题类:

1
2
3
4
5
6
public abstract class Subject {
/**
*一个普通的业务方法
*/
public abstract void visit();
}

真实主题类:

1
2
3
4
5
6
7
public class RealSubject extends Subject {

public void visit() {
//RealSubject中visit的具体实现
System.out.println("Real Subject!");
}
}

代理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ProxySubject extends Subject {
//持有真实主题的引用
private RealSubject mSubject;

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

public void visit() {
//通过真实主题引用的对象调用真是主题中逻辑方法
mSubject.visit();
}
}

客户类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public classClient {
public static void main(String[] args) {
//构造一个真实主题对象
RealSunject real = new RealSubject();

//通过真实主题对象构造一个代理对象
ProxySubject proxy = new ProxySubject(real);

//调用代理的相关方法
proxy.visit();

}
}

以上就是代理模式的通用代码。

三、代理模式的简单实现

我们模拟一个打官司的例子,具体的代码如下。

诉讼接口类:

1
2
3
4
5
6
7
8
9
10
public interface ILawsuit {
//提交申请
void submit();
//进行举证
void burden();
//开始辩护
void defend();
//诉讼完成
void finish();
}

可以看到,里面有四个诉讼常规的流程。我们在接口定义四个抽象方法。

具体诉讼人。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class XiaoMin implements ILawsuit {
public void submit () {
System.out.println("老板欠工资,特此申请仲裁!");
}

public void burden () {
System.out.println("这是合同书和过去一年银行流水");
}

public void defend () {
System.out.println("证据确凿,抵赖也没有用~");
}

public void finish () {
System.out.println("诉讼成功,判决老板即日起七天内结算工资!");
}
}

如上所述,类实现ILawsuit接口并实现四个抽象方法,实现具体的业务逻辑,接下来是代理类。

代理律师:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Lawyer implements ILawsuit {
private ILawsuit mLawsuit;

public Lawyer(ILawsuit lawsuit) {
mLawsuit = lawsuit;
}

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

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

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

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

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

客户类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Client {
public static void main(String[] args) {
//构造一个小民
ILawsuit xiaomin = new XiaoNin();
//构造一个代理律师并将小民作为参数传入
ILawsuit lawyer = new Lawyer(xiaomin);
//律师提交申请
lawyer.submit();
//律师举证
lawyer.burden();
//律师辩护
lawyer.defend();
//完成诉讼
lawyer.finish();

}
}

这就代理模式,这是一种委托机制,真是对象把方法的执行委托给代理对象,所以代理模式也叫委托模式,代理类也可以代理多个被代理类,就像上面的例子一样,律师可以代理多个人打官司,这也符合现实状况,而具体代理哪个人,则要看代理类持有的实际对象类型。

代理小辉打官司可以吗?当然可以,我们只需要再定义一个XiaoHui类即可,再在客户类新增小辉这个类的引用,作为参数传入代理类的构造方法中。

四、静态代理和动态代理

代理模式分为静态代理和动态代理,静态代理如上述例子那样,代理者的代码由程序员自己或者通过一些自动化的工具生成固定的代码再对其进行编译,也就是说我们的代码运行前代理类的class编译文件就已存在;而动态代理则相反,通过反射机制动态地生成代理者的对象,也就是说我们在code阶段压根不需要知道代理谁,代理谁我们将在执行阶段决定,而Java也给我们提供了一个便捷的动态接口InvocationHandler,实现该接口要复写invoke()方法。

1
2
3
4
5
6
public class DynamicProxy implements InvocationHandler {
public Object invoke (Object args 0,Method args1,Object[] args2) throws Throwable{
return null;
}

}

我们通过invoke方法来调用具体的被代理方法,也就是真实的方法,动态代理可以使我们的代码更简洁,不过我们先要完善代理类。

动态代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
public class DynamicProxy implements InvocationHandler {
private Object obj;

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

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

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

修改后的客户类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void main (String[] args) {
//构造一个小民
ILawsuit xiaomin = new XiaoMn();

//构造一个动态代理
DynamicProxy proxy = new DynamicProxy(xiaomin);

//获取被代理类小民的ClassLoader
ClassLoader loader = xiaomin.getClass().getClassLoader();

//动态构造一个代理者律师
ILawsuit lawyer = (ILawsuit) proxy.newProxyInstance(loader,new Class[]{ILawsuit.class},proxy);

//律师提交诉讼
lawyer.submit();

//律师进行举证
lawyer.bueden();

//律师替小民进行辩护
lawyer.defend();

//完成诉讼
lawyer.finish();

}

结果和以前一致,由此可见动态代理可以通过一个代理类来代理N个被代理类,其实质是对代理者和被代理者进行解耦,使两者没有直接的耦合关系,相对而言静态代理则只能为给定接口下的实现类进行代理,如果接口不同那就需要重新定义不同的代理类,较为复杂,不过静态代理类更符合面向对象原则,在开发时具体使用哪种方式,看我们的业务模式和自己的偏好了。

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

  • 远程代理
    为对象在不同的内存地址空间提供局部代理。使系统可以将server部分的实现隐藏,以便client可以不必考虑server的存在
  • 虚拟代理
    使用一个代理对象表示一个十分耗资源的对象并在真正需要时才创建
  • 保护代理
    使用代理控制对原始对象的访问。该类型的代理常被用于原始对象有不同访问权限的情况
  • 智能引用
    在访问原始对象时执行一些自己的附加操作并对指向原始对象的引用计数
四、Android源码中的代理模式

Android源码中有不少使用代理模式的设计,比如源码中ActivityManagerProxy代理类,具体代理的是a、ActivityManagerService的子类ActivityManagerService。ActivityManagerProxy与ActivityManagerNative这个类在同一文件中。

1
2
3
class ActivityManagerProxy implements IActivityManager {

}

ActivityManagerProxy实现了IActivityManager接口,该接口定义了一些Activity相关的接口方法,其中有一些我们平时开发也常用到的。

1
2


IActivityManager接口类就相当于是代理类中的抽象主题,真正的实现主题是谁呢?就是我们提到的继承于ActivityManagerNative的ActivityManagerService类,这几个类之间的大致关系如下:


如上UML图我们可知,ActivityManagerProxy和ActivityManagerNative都实现了IActivityManager,严格来说,ActivityManagerProxy就是代理部分,而ActivityManagerNative就是真是部分,不过ActivityManagerNative是个抽象类,并不处理具体逻辑,大部分逻辑的实现都由其子类ActivityManagerService承担,这也是我们说真是部分是ActivityManagerService而不是ActivityManagerNative,ActivityManagerService是系统级的Service并且运行于独立的进程空间中,可以通过ServiceManager来获取它,而AMP也运行于自己所处的进程空间中,两者并不相同,因此AMP和AMS的通信必定是通过进程间通信来实现的,这里的跨进程实现是基于Android的Binder机制,同时通过上图我们对代理模式的4种适应场景可以判断,此处源码所实现的代理实质为远程代理。AMP在实际的逻辑处理中并未过多地被外部类使用,因为在Android中管理与维护activity相关信息的类是另外一个叫做ActivityManager的类,ActivityManager虽说管理着activity的信息,但是实质大部分逻辑是由AMP承担,以getAppTasks为例,ActivityManager的getAppTasks的逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public List<ActivityManager.AppTask> getAppTasks() {
ArrayList<AppTask> tasks = new ArrayList<AppTask>();
List<IBinder> appTasks;
try {
appTasks = getService().getAppTasks(mContext.getPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
int numAppTasks = appTasks.size();
for (int i = 0; i < numAppTasks; i++) {
tasks.add(new AppTask(IAppTask.Stub.asInterface(appTasks.get(i))));
}
return tasks;
}

可以看到,逻辑很简单,只是简单地调用了ActivityManager的getService方法获取了一个IActivityManager类型的对象,再调用getAppTasks方法。

注意:在API 26之后,ActivityManagerProxy被移除了,取而代之的是AIDL方式,