设计模式学习(八)——代理模式

代理模式定义

代理模式(Proxy Pattern)为另一个对象提供一个替身或占位符以控制对这个对象的访问。

在一些情况下,客户端不想或不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介作用,去掉客户不能看到的内容和服务或者增添客户需要的额外服务。

最常见的代理模式有以下三种:

  • 远程代理:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一台主机中,也可以在另一台主机中。远程代理又称为大使(Ambassador)

  • 虚拟代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象,真实对象只在需要时才会被真正创建。

  • 保护代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。

代理模式能够协调调用者和被调用者,能够在一定程度上降低系统的耦合度。


代理模式结构图

图中:

  • Subject表示抽象主题角色,它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题;

  • Proxy表示代理主题角色,代理主题角色内部含有对真实主题的引用,从而可以再任何时候操作真是主题对象,它可以控制真实主题的应用;

  • RealSubject表示真实主题角色,它定义了代理角色所代表的真实对象。


代理模式实例——日志记录代理

实例说明

在某应用软件中需要记录业务方法的调用日志,在不修改现有业务类的基础上为每一个类提供一个日志记录代理类,在代理类中输出日志,如在业务方法method()调用之前输出“方法method()被调用,调用时间为2010-10-10 10:10:10”,调用之后如果没有抛异常则输出“方法method()调用成功”,否则输出“方法method()调用失败”。在代理类中调用真实业务类的业务方法,使用代理模式设计该日志记录模块的结构,绘制类图并编程模拟实现。

实例类图

实例类图中与代理模式结构图对应的类主要有:

  • AbstractLog接口充当抽象主题;

  • LoggerProxy类充当代理主题;

  • BusinessClass类充当真实主题。

实例代码

AbstractLog.java:

//抽象日志记录类:抽象主题
interface AbstractLog
{
public void method();
}

LoggerProxy.java:

import java.util.*;

//日志记录代理类:代理主题
class LoggerProxy implements AbstractLog
{
private BusinessClass business;

public LoggerProxy()
{
business = new BusinessClass();
}

public void method()
{
Calendar calendar = new GregorianCalendar();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR) + 12;
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
String dateTime = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second + "!";
System.out.println("方法method()被调用,调用时间为" + dateTime);
try{
business.method();
System.out.println("方法method()调用成功!");
}
catch(Exception e)
{
System.out.println("方法method()调用失败!");
}
}
}

BusinessClass.java:

//业务类:真实主题
class BusinessClass implements AbstractLog
{
public void method()
{
System.out.println("真实业务方法!");
}
}

Client.java:

//客户端测试类
class Client
{
public static void main(String args[])
{
AbstractLog al;
al = new LoggerProxy();
al.method();
}
}

运行结果

附加描述

在本实例中,通过代理类LoggerProxy来间接调用真实业务类BusinessClass的方法,可以在调用真实业务方法时增加新功能(如日志记录),此处使用的是代理模式的一种较为简单的形式,类似于保护代理,在实施真实调用时可以执行一些额外的操作。由于代理主题和真实主题实现了相同的接口,因此在客户端可以针对抽象编程,而将具体代理类类名存储至配置文件中,增加和更换代理类和真实类都很方便,无需修改源代码,满足开闭原则。


后话

之前对代理一知半解,这次趁着学习代理模式也对代理更有所认知了。

代理的英文有好多个,agent、proxy、stub和surrogate是比较常见到的几个英文。