本文为原创博文,转载请注明出处,侵权必究!json
一、初识装饰器模式设计模式
装饰器模式,顾名思义,就是对已经存在的某些类进行装饰,以此来扩展一些功能。其结构图以下:框架
二、最简单的代码实现装饰器模式ide
//基础接口 public interface Component { public void biu(); } //具体实现类 public class ConcretComponent implements Component { public void biu() { System.out.println("biubiubiu"); } } //装饰类 public class Decorator implements Component { public Component component; public Decorator(Component component) { this.component = component; } public void biu() { this.component.biu(); } } //具体装饰类 public class ConcreteDecorator extends Decorator { public ConcreteDecorator(Component component) { super(component); } public void biu() { System.out.println("ready?go!"); this.component.biu(); } }
这样一个基本的装饰器体系就出来了,当咱们想让Component在打印以前都有一个ready?go!的提示时,就可使用ConcreteDecorator类了。具体方式以下:函数
//使用装饰器 Component component = new ConcreteDecorator(new ConcretComponent()); component.biu(); //console: ready?go! biubiubiu
三、为什么使用装饰器模式this
一个设计模式的出现必定有他特殊的价值。仅仅看见上面的结构图你可能会想,为什么要兜这么一圈来实现?仅仅是想要多一行输出,我直接继承ConcretComponent,或者直接在另外一个Component的实现类中实现不是同样吗?spa
首先,装饰器的价值在于装饰,他并不影响被装饰类自己的核心功能。在一个继承的体系中,子类一般是互斥的。好比一辆车,品牌只能要么是奥迪、要么是宝马,不可能同时属于奥迪和宝马,而品牌也是一辆车自己的重要属性特征。但当你想要给汽车喷漆,换坐垫,或者更换音响时,这些功能是互相可能兼容的,而且他们的存在不会影响车的核心属性:那就是他是一辆什么车。这时你就能够定义一个装饰器:喷了漆的车。无论他装饰的车是宝马仍是奥迪,他的喷漆效果均可以实现。设计
再回到这个例子中,咱们看到的仅仅是一个ConcreteComponent类。在复杂的大型项目中,同一级下的兄弟类一般有不少。当你有五个甚至十个ConcreteComponent时,再想要为每一个类都加上“ready?go!”的效果,就要写出五个子类了。毫无疑问这是不合理的。装饰器模式在不影响各个ConcreteComponent核心价值的同时,添加了他特有的装饰效果,具有很是好的通用性,这也是他存在的最大价值。日志
四、实战中使用装饰器模式code
写这篇博客的初衷也是刚好在工做中使用到了这个模式,以为很是好用。需求大体是这样:采用sls服务监控项目日志,以Json的格式解析,因此须要将项目中的日志封装成json格式再打印。现有的日志体系采用了log4j + slf4j框架搭建而成。调用起来是这样的:
private static final Logger logger = LoggerFactory.getLogger(Component.class); logger.error(string);
这样打印出来的是毫无规范的一行行字符串。在考虑将其转换成json格式时,我采用了装饰器模式。目前有的是统一接口Logger和其具体实现类,我要加的就是一个装饰类和真正封装成Json格式的装饰产品类。具体实现代码以下:
/** * logger decorator for other extension * this class have no specific implementation * just for a decorator definition * @author jzb * */ public class DecoratorLogger implements Logger {
public Logger logger;
public DecoratorLogger(Logger logger) {
this.logger = logger;
}
@Override public void error(String str) {} @Override public void info(String str) {} //省略其余默认实现 }
/** * json logger for formatted output * @author jzb * */ public class JsonLogger extends DecoratorLogger { public JsonLogger(Logger logger) { super(logger); } @Override public void info(String msg) { JSONObject result = composeBasicJsonResult(); result.put("MESSAGE", msg); logger.info(result.toString()); } @Override public void error(String msg) { JSONObject result = composeBasicJsonResult(); result.put("MESSAGE", msg); logger.error(result.toString()); } public void error(Exception e) { JSONObject result = composeBasicJsonResult(); result.put("EXCEPTION", e.getClass().getName()); String exceptionStackTrace = ExceptionUtils.getStackTrace(e); result.put("STACKTRACE", exceptionStackTrace); logger.error(result.toString()); } public static class JsonLoggerFactory { @SuppressWarnings("rawtypes") public static JsonLogger getLogger(Class clazz) { Logger logger = LoggerFactory.getLogger(clazz); return new JsonLogger(logger); } } private JSONObject composeBasicJsonResult() { //拼装了一些运行时信息 } }
能够看到,在JsonLogger中,对于Logger的各类接口,我都用JsonObject对象进行一层封装。在打印的时候,最终仍是调用原生接口logger.error(string),只是这个string参数已经被咱们装饰过了。若是有额外的需求,咱们也能够再写一个函数去实现。好比error(Exception e),只传入一个异常对象,这样在调用时就很是方便了。
另外,为了在新老交替的过程当中尽可能不改变太多的代码和使用方式。我又在JsonLogger中加入了一个内部的工厂类JsonLoggerFactory(这个类转移到DecoratorLogger中可能更好一些),他包含一个静态方法,用于提供对应的JsonLogger实例。最终在新的日志体系中,使用方式以下:
private static final Logger logger = JsonLoggerFactory.getLogger(Component.class); logger.error(string);
他惟一与原先不一样的地方,就是LoggerFactory -> JsonLoggerFactory,这样的实现,也会被更快更方便的被其余开发者接受和习惯。