设计模式笔记(十八)– 代理模式

​代理模式(Proxy Pattern)允许我们为真实对象创建代理,如果您想与某个对象对话,将通过其代理与其对话,此代理程序接收您的消息并将其转发给目标对象。

它有什么好处?允许我们在将消息发送至目标对象前执行一些感兴趣的任务,例如日志、权限控制、缓存等。


问题提出

假设我们要构建一个电子书阅读程序,它有一个图书库,我们购买的所有的电子书均在此图书库中。

创建Ebook类,它有私有属性fileName,私有方法load用于加载书的内容,在构造函数中调用load方法初始化加载书的内容。show方法用于显示书的内容。getFileName方法用于获取书名。

创建Library类,用于保存电子书。我们使用Map存储,方便检索。add方法用于添加电子书,openEbook方法用于打开某本电子书。

到Main进行测试。创建一个Library对象,加载a、b、c三本电子书。然后打开电子书 a。

如果我们有几百本电子书,我们的程序也会将所有的书从磁盘里读出并存储到内存里,正如这个示例展示的,我们并不会打开每一本书的,我们很可能只会打开阅读其中的一本书,显然加载全部的电子书是不必要的成本开销。

这就是代理模式发挥作用的地方。

利用代理模式,我们可以创建一个模拟真实电子书(Ebook)的对象,但它只在我们需要阅读时才加载到内存。


解决方案

当前我们的结构是这样的。

Library类持有Ebook,即会将所有Ebook加载到内存。我们修改这个结构添加一个代理EbookProxy,使其只在我们需要的时候才加载,这个结构被称为延迟初始化。

这种方法用在很多框架里,比如hibernate框架。

我们来整理其层次结构。

我们引入Ebook接口,它定义了show()方法,实际的电子书RealEbook和代理EbookProxy均实现了该接口。EbookProxy有一个私有属性ebook,代表实际的电子书对象。这个私有属性ebook是个关键点,我们不会一开始就加载电子书内容,而是初始化为null。在EbookProxy的show方法里,我们检查ebook是否是实际的电子书对象,如果不是,就创建它,然后再调用实际电子书的show方法。

利用这种结构,我们仅在我们需要的时候才真正加载电子书。一开始仅列出电子书名称并不加载到内存。在实际需要打开电子书时,才从磁盘读出电子书并加载。

Library类仅依赖Ebook接口,符合开放-闭合原则,我们可以有不同的Ebook接口的代理实现。

目前我们的代理实现了延迟初始化,我们也可以创建其他我们感兴趣的代理,比如授权:我们检查电子书的租赁期是否到期,如果到期的话我们将不允许访问该电子书;比如日志记录:我们记录其每一步操作。

这种结构就被称为代理模式(Proxy Pattern)。

四人帮GOF的经典命名结构是这样的。

我们看到Proxy的request的方法里,我们可以执行一些感兴趣的任务,然后调用实际的RealSubject的request方法。

这种模式的重点是代理Proxy对象看起来跟实际对象是一样的,因为它们都实现了相同的接口,代理可以做任何它感兴趣的事情。


代码实现

首先,我们要抽取出实际电子书和代理电子书一致的接口。

将原Ebook类改名为RealEbook。创建接口Ebook,定义了getFileName和show两个方法。实际电子书RealEbook和电子书代理EbookProxy都需要实现Ebook接口。

以下是Ebook接口和RealEbook类。

创建电子书代理类EbookProxy,实现接口Ebook。它有fileName属性并在构造函数中进行初始化,另外还有一个ebook属性表示实际的电子书对象RealEbook。

这里有个关键点,ebook初始保持为null。在实现的show方法里,判断ebook是否为null,如果是则创建RealEbook对象,然后调用ebook对象的show方法。

到Main类进行测试。我们只需用电子书代理EbookProxy来替换原来的实际电子书即可。

我们看到,我们的图书库里有a、b、c三本书,我们只打开a书,实际也只加载了a书,说明它只加载需要打开的电子书,达到了我们的目的。

代理模式是符合开放闭合原则的。比如要实现日志记录的功能,我们只需创建新的代理类即可,不必修改原有的代码。

创建类LoggingEbookProxy,实现Ebook接口。像EbookProxy一样,我们添加fileName、ebook属性,在show方法里,添加日志功能。

回到Main类测试。我们只需将EbookProxy替换成LoggingEbookProxy,就有日志记录的功能了。

我们通过添加新类而不是修改原有的代码改变了应用程序的行为,这就是开放闭合的原则。


小结

代理模式(Proxy Pattern)允许我们为真实对象创建代理,并在将消息发送至目标对象前执行一些感兴趣的任务。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注