开始之前
本章节是一个系列,里面用的的代码实例都是连贯的。在实现某一种设计模式时,为了减少代码篇幅,前面博客出现model类(仅限公用的model类,比如compute、CPU、Mem、Disk等纯对象类)不会重复出现,读者在阅读某一篇博客时,如果发现突然出现了一个新的model类,在本片博客中没有其定义,可以往前面的博客翻一下!
最后,当本系列更新完成后,我会整个的代码完整贴出来,提供下载链接!
代理模式
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
介绍
意图: 为其他对象提供一种代理以控制对这个对象的访问。
主要解决: 在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
如何解决: 增加中间层。
应用实例:
1、Windows 里面的快捷方式。
2、买火车票不一定在火车站买,也可以去代售点。
3、一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。
4、spring aop。
优点: 1、职责清晰。 2、高扩展性。 3、智能化。
缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
使用场景:
按职责来划分,通常有以下使用场景:
1、远程代理。
远程代理主要处理不在本地计算机的对象。代理对象扮演着远程对象的本地代表,所有对远程对象的操作都通过代理对象完成。webservice、EJB、dubbo等分布式技术。
2、虚拟代理。
虚拟代理用于处理大规模资源的对象,比如大图像或者网络资源。在真正需要时,再去创建或加载这个资源。
3、Copy-on-Write 代理。
它是虚拟代理的一种,把克隆操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
4、保护(Protect or Access)代理。
保护(Protect or Access)代理用于控制真实对象的访问权限,只有通过安全检查的请求才能访问真实对象。比如,有些对象有对其访问和操作的权限限制,我们可以把权限的处理逻辑放到代理中。
5、Cache代理。
为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。从而可以避免某些方法的重复执行,优化系统性能。
6、防火墙(Firewall)代理。
7、同步化(Synchronization)代理。
是一种用于在多线程环境中保持对象状态一致性的机制。它通常用于懒加载或延迟初始化的上下文中。在这种代理中,方法调用会被转发到实际的对象,并通过某种同步机制确保线程安全。
8、智能引用(Smart Reference)代理。
智能指引是为一个对象提供一种代理,并在一些特定的情况下选择提供一种“智能”的操作方式。比如引用计数,如果一个对象进行指向操作,其引用计数+1;如果进行解引用操作,其引用计数-1,等到引用计数为0时,可以自动回收对象。
注意事项:
- 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
- 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
主要涉及到以下几个核心角色:
抽象主题(Subject):
- 定义了真实主题和代理主题的共同接口,这样在任何使用真实主题的地方都可以使用代理主题。
真实主题(Real Subject):
- 实现了抽象主题接口,是代理对象所代表的真实对象。客户端直接访问真实主题,但在某些情况下,可以通过代理主题来间接访问。
代理(Proxy):
- 实现了抽象主题接口,并持有对真实主题的引用。代理主题通常在真实主题的基础上提供一些额外的功能,例如延迟加载、权限控制、日志记录等。
代码实现
同步代理
现在模拟一个IO
public interface FileIO {
int read();
int write();
}
public class FileIOImpl implements FileIO{
private int size;
@Override
public int read() {
size--;
System.out.println("read size:"+size);
return size;
}
@Override
public int write() {
size++;
System.out.println("write size:"+size);
return size;
}
}
如果直接进行FileIO进行访问,那么多线程情况下,size的访问是不安全的。
这个是需要一个代理实现同步作用的同时,原来的代码也不需要改动。
public class SyncFileIO {
private FileIO fileIO;
public SyncFileIO(FileIO fileIO)
{
this.fileIO = fileIO;
}
public synchronized int read()
{
return fileIO.read();
}
public synchronized int write()
{
return fileIO.write();
}
}
public class Client {
public static void main(String[] args) {
FileIO fileIO = new FileIOImpl();
//通过syncfileIO代理fileIO,保证线程安全
SyncFileIO syncFileIO = new SyncFileIO(fileIO);
syncFileIO.write();
syncFileIO.read();
}
}
保护代理(也叫安全代理或者访问控制代理)
保护代理在spring框架中用的比较多,假设现在有一个请求进来,在请求处理之前需要判断这个请求是否合法
public interface Request {
void request();
}
public class GetRequest implements Request {
@Override
public void request(){
System.out.println("get request");
}
}
没有代理的情况下,就需要修改GetRequest
这个类的处理逻辑,通过代理就可以不需要改动GetRequest
这个类的处理逻辑,
public class RequestProxy {
Request request;
public RequestProxy(Request request) {
this.request = request;
}
private void beforeReques(){
System.out.println("proxy before");
}
public void request(){
beforeReques();
request.request();
afterRequest();
}
public void afterRequest(){
System.out.println("proxy after");
}
}
public class Client {
public static void main(String[] args) {
Request request = new GetRequest();
RequestProxy requestProxy = new RequestProxy(request);
requestProxy.request();
}
}