代理模式
1.代理模式介绍
代理模式也称委托模式,是一种结构型设计模式。
2.代理模式的定义
代理模式是在不改变原始类的情况下,通过引入代理类实现对原始类附加新的能力.
3.代理模式的使用场景
当无法或不想直接访问某个对象或者访问某个对象存在困难时可以通过一个代理对象间接访问。为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。
4. 实现方式
代理模式的实现根据不同的条件,实现的方式也不同.
- 原始类是我们自己维护的,那么我们可以使用基于接口而非实现的思想实现.步骤:1.代理类与原始类实现同一个业务接口;2. 代理类实现与原始类组合;3. 在代理类中实现对原始类的访问。
- 原始类是第三方提供的,那么我们只能通过实现的方式实现.步骤:1. 代理类继承至原始类;2.在代理类中实现对原始类的访问
5.代理模式的类图
- Subject:抽象主题类,该类的主要职责是声明真实主题与代理的共同接口,该类既可以是一个抽象类也可以是一个接口。
- RealSubject:真实主题类
该类也称为被委托类或被代理类,该类定义了代理所表示的真实对象,由其执行具体的业务逻辑方法,而客户类则通过代理类间接的调用真实主题类中定义的方法。 - ProxySubject:代理类,该类称为委托类或者代理类,该类持有一个对真实主题类的引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行,以起到代理的作用。
- Client:客户端,即使用代理类的类型
// Subject
public interface Image {
void display();
}
// RealSubject
public class RealImage implements Image {
private String mFileName;
public RealImage(String fileName) {
this.mFileName = fileName;
loadFromDisk();
}
@override
public void display() {
System.out.println("Display:" + mFileName);
}
private void loadFromDisk() {
System.out.println("Loading:" + mFileName);
}
}
// ProxySubject
public class ProxyImage implements Image {
private RealImage realImage;
private String mFileName;
public ProxyImage(String fileName) {
this.mFileName = fileName;
}
@override
public void display() {
if(this.realImage == null) {
this.realImage = new RealImage(mFileName);
}
this.realImage.display();
}
}
// Client
public class Client {
public static void main(String[] args) {
Image image = new ProxyImage("tupian");
image.display();
}
}
6.代理模式的简单实现
代理模式可以大致分为两个部分,一是静态代理,二是动态代理,
- 静态代理如上述实例那样,代理类由我们或通过一些自动化工具生成固定的代码再对其进行编译,在运行时将原始类替换成代理类即可
- 动态代理通过反射机制动态地生成代理者对象,也就是说我们事先不为每个原始类编写代理类,而是在运行的时候,动态地创建原始类对应的代理类,在运行是将原始类替换成代理类.
7.Android中的Binder跨进程通信机制与AIDL
在编码世界里,两个对象能直接相互访问的前提是这两个对象都存在于相同的内存地址空间,如果两个对象分别存在于两个不同的进程中,比如ActivityManager和ActivityManagerService,那么这两个对象是不能直接互相调用的,这时我们就需要使用到一种跨进程通信技术,使存在于两个不同进程的对象能够相互访问.传统的跨进程通信方式有很多,比如Socket、信号量、管道、内存共享、消息队列等。这些是传统的Linux跨进程通信。Android是基于Linux的,按照道理来讲,这些进程间通信都可以,为什么它还有自己去设计一个进程间通信-Binder那?这只能说明Binder具有其他通信方式无可比拟的优势.传统的跨进程通信机制,如Scoket,开销大且销量不高,而管道和队列拷贝次数多,更重要的是对于移动设备来说,安全性相当重要,而传统的通信机制安全性低,大部分情况下接受方无法得到发送方进程的可信PID/UID。而Binder在确保传输性能的同时又提高了安全性。
Binder是一种基于C/S(Client/Server)通信机制的框架。
在讲解Binder之前我们首先对Binder所涉及的4个主要模块大致的了解一下,他们分别是Binder Client、Server、ServerManager和 Driver,这四者之间类似于网络访问。
Client相当于我们的客户端, Server相当于我们的服务器,ServerManager相当于DNS服务器,而 Driver相当于一个路由器。
其中Driver的实现在内核空间中,其余三者Binder Client、 Server、ServerManager的实现在用户空间中。
Driver位于内核空间中,其以字符设备中的misc类型注册,用户可以从/dev/binder设备文件节点上,通过open和ioctl文件操作函数与 Driver进行通信。其主要负责Binder通信的建立,以及其在进程间的传递和Binder引用计数器管理/数据包的传输等。Binder Client和Binder Server之间的跨进程通信则统一通过Binder Driver处理转发。
Binder Client,只需要知道自己要使用的Binder的名字以及Binder实体在ServerManager中的0号引用即可.访问的原理也很简单,Binder Client先是通过0号引用去访问ServerManager获取该Binder的引用,得到引用后就可以像普通方法调用那样去调用Binder实体的方法。最后我们的ServerManager则用来管理Binder Server,Binder Client可以通过它来查询Binder Server接口,刚才我们说到Binder Client可以通过ServerManager来获取Binder引用,这个Binder的引用就是由ServerManager转化的,Binder Server在生成一个Binder实体的同时会为其绑定一个名字并将这个名字封装成一个数据包传递给Binder Driver,Binder Driver接收到这个数据包后,如果发现这个Binder是新传进来的,那么就会为其在内核空间中创建相应的Binder实体节点和一个对该实体节点的引用。这个实体节点在相应的源码中叫做Binder_node而其引用叫Binder_ref,创建完毕后,Binder Driver就会将该引用传递给ServerManager,ServerManager收到后就会从中取出该Binder的名字和引用插入一张数据表中,这和DNS中存储的域名到IP地址的映射原理类似,而对于网络访问来说,而DNS服务器也并不一定对每一个IP地址都有域名映射的记录,我们常常也会碰到直接通过IP地址访问服务器的情况,而Binder也一样并非一定要在ServerManager中有记录,很多时候Binder Server会将一个Binder实体封装进数据包传递给Binder Client,而此时Binder Server会在该数据包中标注Binder实体的位置,Binder Driver则会为该匿名的Binder生成实体节点和实体引用,并将该引用传递给Binder Client。ServerManager就是一个标准的Binder Server,并在Android中约定其在Binder通信的过程中唯一表示(类似于ip地址)永远都是0.
8.总结
代理模式应用广泛,代理模式几乎没有什么缺点,它是细分化至很小的一种模式。