平常使用的设计模式中,经常可以遇见代理模式,而装饰器模式比较少见,但这两种模式还是有很多相同之处。
当代码中需要使用某些对象,但代码中不合适或者不能直接使用使用这些对象,例如mybatis 中可以兼容很多日志,拿其中的log4j2 来说,mybatis肯定不好直接使用log4j2的输出日志对象去输出日志,那样框架就直接依赖log4j2,那么在框架中就是使用了自己的代理类Log4j2AbstractLoggerImpl 去实现通用的log 接口,通过然后在代理类中获log4j2的日志对象,mybatis 所有日志输出交由自己的代理去处理,然后代理类交给log4j2.这样就减少了代码的耦合,不用直接操作log4j2 对象。用下面代码说明
//log4j2日志接口
public interface Log{
public void info();
public void error();
public void debug();
}
//log4j2实现类
public class Log4j2LoggerImpl{
public void info(){
System.out.println(“输出info级别日志”);
}
public void error(){
System.out.println(“输出错误级别日志”);
}
public void debug(){
System.out.println(“输出错调试级别日志”);
}
}
//mybatis 代理类
public class MybatisProxy implements Log{
private Log log;
public MybatisProxy(Log log){//只在代理类这一处依赖log4j2,
This.log = new Log4j2LoggerImpl();
}
public void info(){
log.info();
}
public void error(){
log.error();
}
public void debug(){
log.debug();
}
}
//mybatis 需要输出日志地方
public class excutor(){
private Log log = new MybatisProxy();//使用代理类的地方不用知道他具 体代理的是哪个类,只管调用,具体的实现交由代理类去实现,相当于解 耦了框架与日志类的依赖,只依赖自己的代理类,这样切换日志比较方便
public List<Map<String, Object>> query(){
log.info();
}
}
第二种情况装饰器适用,就是想要对某个接口的实现类某个方法进行扩展,而不改变原来接口的方法,例如在上面这个例子中我主要功能是用于对输入日志的改造,在输出日志之前执行某个操作代码如下
//log4j2日志接口
public interface Log{
public void info();
public void error();
public void debug();
}
//log4j2实现类
Public class Log4j2LoggerImpl1 implements Log{
public void info(){
System.out.println(“Log4j2LoggerImpl1输出info级别日志”);
}
public void error(){
System.out.println(“Log4j2LoggerImpl1输出错误级别日志”);
}
public void debug(){
System.out.println(“Log4j2LoggerImpl1输出错调试级别日志”);
}
}
//log4j2实现类
Public class Log4j2LoggerImpl2 implements Log {
public void info(){
System.out.println(“Log4j2LoggerImpl2输出info级别日志”);
}
public void error(){
System.out.println(“Log4j2LoggerImpl2输出错误级别日志”);
}
public void debug(){
System.out.println(“Log4j2LoggerImpl2输出错调试级别日志”);
}
}
//mybatis 装饰模式
Public class MybatisDerector implements Log{
private Log log;
public MybatisProxy(Log log){//传入需要装饰的对象,一般可能装饰多个不同的方法,由调用多少个实现类System.out.println(“改造info方法”)
This.log = log;
}
public void info(){
System.out.println(“装饰器自己的方法”);
log.info();
}
public void error(){
System.out.println(“装饰器自己的方法”);
log.error();
}
public void debug(){
System.out.println(“装饰器自己的方法”);
log.debug();
}
}
//mybatis 需要输出日志地方
public class excutor(){
//使用地方需要用户自己去指定具体的装饰的那一个类
private Log log1 = new MybatisProxy(new Log4j2LoggerImpl1());
private Log log2= new MybatisProxy(new Log4j2LoggerImpl2());
public List<Map<String, Object>> query(){
log1.info();
log2.info();
}
}
与上面比对下可以知道,代理模式下需要代理的类在代理类初始化的时候就确定了,只有代理能知道是代理那个个类,而调用者不用关心代理类所代理的是哪个类,侧重于代码的解耦。而装饰器模式下,在装饰器类初始化的时候,需要调用地方传给具体的需要装饰的类,一般可能有多个实现类,装饰多个。因此我们可以给装饰器装饰不用的实现类。
还有一个我自己的猜想。其实通过这两个类的名字,代理的意思就是通过中间人去完成某一项功能,而我不用关注这个功能是谁完成的,就像上面例子中对象在代理类内初始化,调用者看不到具体实现类,因此代理倾向于是解耦,然调用者不用关心具体的实现来。而装饰的意思是对装饰类方法的升级改造,而不改变方法的本身,对类进行升级,例如上面例子中在装饰方法的前置后置添加自己的逻辑。
不过代理模式也多用于前置后置处理例如spring 的aop , mybatis的插件功能。其实代理模式和装饰器模式都是可以对于代理类和装饰类的方法进行前后置处理,这一点都可以实现。我想两者最根本的区别就是调用者需不需要知道具体实现类这块把。