定义
为其他对象提供一个代理以控制对这个对象的访问
演化
1.我们平时写代码的时候,免不了打印一些日志出来,方便调试。
Log.d("TAG", "Somethings")
不过,这种打印日志的方式,一般只在测试期间使用。当发布正式版本时候,需要关闭日志功能。因此,我们一般会这么写:
if (Constants.debug) {
Log.d("TAG", "Somethings")
}
然后通过修改 Constans.debug的值来控制日志的打印。
2.假设程序中存在着成千上万处日志输出,那么按照1中的写法,就会存在着一些问题:
- 每次新增日志输出,都需要额外增加对debug的判断。
- 当有一天不再使用Log打印日志,而是换成Logger时,代码的修改量非常大。
为了解决这两个问题,我们新建一个工具类来管理一下Log。
public class LogUtil {
public static boolean debug = true;
public static void d(String tag, String content) {
Log.d(tag, content);
}
}
打印日志时,不再直接调用Log的方法,而是通过LogUtil调用。LogUtil跟Log的方法一一对应,这些方法的功能就是去调用Log中对应的方法。此时如果想要通过debug对日志进行控制,只需要将控制的逻辑添加在LogUtil的方法中即可。
public class LogUtil {
public static boolean debug = true;
public static void d(String tag, String content) {
if(debug) {
Log.d(tag, content);
}
}
}
如果想要替换别的日志库Logger,也只需要稍微做下修改
public class LogUtil {
public static boolean debug = true;
public static void d(String tag, String content) {
if(debug) {
Logger.d(tag, content);
}
}
}
这时无论是添加统一的控制逻辑,还是功能替换,都十分方便,只需要改一下LogUtil即可。这就是代理思想的一种简单运用:通过一个间接的类对象去访问实际对象,可以在间接类中附加各种用途。我们称这个间接类为代理类。
3.在2中,存在一个问题。如果代理类与被代理的类之间方法名不同,调用方就很难判断出自己需要的方法究竟是哪个一个。因此需要让代理类与被代理类实现同一个接口,来保证两者拥有相同的特征。
(1) 定义统一的接口
public interface Log {
void d(String tag, String content);
}
(2) 定义实现类Logger
public class Logger implements Log {
@Override
public void d(String tag, String content) {
}
}
(3) 定义代理类LoggerProxy
public class LoggerProxy implements Log {
private final Log log;
public LoggerProxy() {
log = new Logger();
}
@Override
public void d(String tag, String content) {
log.d(tag, content);
}
}
(4) 在代码中使用
Log log = new LoggerProxy();
log.d("test", "content");
这样已经避免了调用者和Logger的直接交互。从代码实现上讲:在Logger中实现打印日志的功能,在LoggerProxy中实现控制日志打印的功能。将一个功能的实现与它的管理分开,解耦。从角色上讲:Logger是提供给调用者的功能,它应该属于服务方。而LoggerProxy控制是否使用日志功能,它属于调用方。因此即使可以通过修改Logger的源码来实现日志的控制逻辑,我们一般也不会这么做。
优点
- 职责清晰
- 容易扩展
- 智能
缺点
- 增加代理,可能会导致程序运行变慢
- 设计时需要做额外的工作,但是维护的时候十分简便
使用场景
- 多处使用某个对象的方法,而这个对象随时可能被改变
- 多处使用某个对象,并自由扩展其方法,但是偶尔要统一添加一些控制功能,对象的基类又无法扩展。