最近在使用日志框架的过程中,有了一些疑惑,这么日志框架,我们如何选择,开发过程中,需要用到的框架很多,不同框架的日志框架一般又不相同,那么如何兼容不同的日志框架呢?
我们知道的日志框架有logback、log4j、logging等。
log4j出现的最早,而logback是对log4j进行了优化,两个框架同是出自Ceki Gülcü之手,其实sl4j也是出自他。
但是我们经常会遇到sl4j
sl4j是简称,全拼应该是Simple Logging Facade for Java,而这里的Facade就是门面模式中的门面
那么什么是门面模式呢?
首先我们可以从字面意思解析,一个医院,一个商场,甚至说一个公司的门面是什么?很简单,前台小姐。为什么要设置前台小姐呢?小姐姐漂亮吗?当然,小姐姐必须的漂亮,毕竟是门面的存在,但是小姐姐不光有颜值还有才华,她对公司的的结构十分清楚,具体到部门的位置,甚至某个职员是否单身(八卦得知(偷笑))。一个陌生人来到公司,想找人或者找某个地方当然和前台小姐姐交流最方便了。
从上面的小例子我们提取出三个元素
陌生人 (客户端) 前台小姐姐(门面) 公司(各个子模块组成)
客户端可以通过门面很容易找到自己需要的东西,而不需要深入了解公司的内部结构,公司不需要在意自己的复杂与否,只需要让前台小姐姐知道调用信息。前台也不关心 公司各个子模块的具体实现,她只需要知道怎么调用即可,如果没有门面模式,客户需要自己熟悉公司的结构,一旦公司结构发生改变,又需要重新熟悉,相互之间依赖过于严重,而门面模式就是为了解决这种问题。
我们回到日志系统
应用程序相当于客户端,抽象层SLF4J相当于门面,只提供接口api,不提供实现,而logback直接实现了Slf4j的api,所以不需要适配层,我们在上面也提到过,logback是对log4j的完善并进行了优化,和slf4j都是出自一个作者,而其他日志框架,想要能够被slf4j调用,就得需要一个适配层了,为什么呢?因为他们本身内部并没有对slf4j的实现,需要一个适配层来完成这项操作(适配器模式以后可以在讨论,大致应该是适配层将其他日志框架绑定到了slf4j提供的api上,已完成可以被slf4j调用的目的)
我们以log4j为例
让我们来写个demo实现下
做为门面接口
package facadeDesigerPattern;
/**
* @author: ggp
* @Date: 2019/1/31 15:14
* @Description:
*/
public interface Sl4j {
void printLog();
void saveLogFile();
}
logback直接实现了接口
package facadeDesigerPattern;
/**
* @author: ggp
* @Date: 2019/1/31 15:17
* @Description:
*/
public class Logback implements Sl4j{
@Override
public void printLog() {
System.out.println("Logback打印日志");
}
@Override
public void saveLogFile() {
System.out.println("logback保存日志文件");
}
/**
* 对外保密
*/
public void secret(){}
}
log4j和它的适配器
package facadeDesigerPattern;
/**
* @author: ggp
* @Date: 2019/1/31 15:21
* @Description:
*/
public class Log4j {
public void printLog(){
System.out.println("Log4j打印日志");
}
public void saveLogFile(){
System.out.println("Log4j保存日志文件");
}
/**
* 对外保密
*/
public void secret(){
}
}
package facadeDesigerPattern;
/**
* @author: ggp
* @Date: 2019/1/31 15:24
* @Description:
*/
public class Log4jAdapter implements Sl4j{
private Log4j log4j;
public Log4jAdapter(Log4j log4j) {
this.log4j = log4j;
}
@Override
public void printLog() {
log4j.printLog();
}
@Override
public void saveLogFile() {
log4j.saveLogFile();
}
}
logging和它的适配器
package facadeDesigerPattern;
/**
* @author: ggp
* @Date: 2019/1/31 15:25
* @Description:
*/
public class Logging {
public void printLog(){
System.out.println("Logging打印日志");
}
public void saveLogFile(){
System.out.println("Logging保存日志文件");
}
/**
* 对外保密
*/
public void secret(){
}
}
package facadeDesigerPattern;
/**
* @author: ggp
* @Date: 2019/1/31 15:26
* @Description:
*/
public class LoggingAdapter implements Sl4j {
private Logging logging;
public LoggingAdapter(Logging logging) {
this.logging = logging;
}
@Override
public void printLog() {
logging.printLog();
}
@Override
public void saveLogFile() {
logging.saveLogFile();
}
}
tinyLog和它的适配器
package facadeDesigerPattern;
/**
* @author: ggp
* @Date: 2019/1/31 15:27
* @Description:
*/
public class TinyLog {
public void printLog(){
System.out.println("TinyLog打印日志");
}
public void saveLogFile(){
System.out.println("TinyLog保存日志文件");
}
/**
* 对外保密
*/
public void secret(){
}
}
package facadeDesigerPattern;
/**
* @author: ggp
* @Date: 2019/1/31 15:27
* @Description:
*/
public class TinyLogAdapter implements Sl4j {
private TinyLog tinyLog;
public TinyLogAdapter(TinyLog tinyLog) {
this.tinyLog = tinyLog;
}
@Override
public void printLog() {
tinyLog.printLog();
}
@Override
public void saveLogFile() {
tinyLog.saveLogFile();
}
}
现在我们通过客户端访问前台小姐姐
package facadeDesigerPattern;
/**
* @author: ggp
* @Date: 2019/1/31 15:42
* @Description:
*/
public class Application {
public static void main(String[] args) {
System.out.println("对前台小姐姐说我要logback的日志");
Sl4j sl = new Logback();
sl.printLog();
sl.saveLogFile();
System.out.println("对前台小姐姐说我要log4j的日志");
Sl4j s2 = new Log4jAdapter(new Log4j());
s2.printLog();
s2.saveLogFile();
System.out.println("对前台小姐姐说我要logging的日志");
Sl4j s3 = new LoggingAdapter(new Logging());
s3.printLog();
s3.saveLogFile();
System.out.println("对前台小姐姐说我要TinyLog的日志");
Sl4j s4 = new TinyLogAdapter(new TinyLog());
s4.printLog();
s4.saveLogFile();
}
}
打印结果
客户端只需要告诉前台小姐姐需求,前台小姐姐只需要去调接口,不需要清楚具体实现,实现了松耦合,而且每个子模块可以保留自己的秘密部分,不对外暴露
使得客户端和子系统之间解耦,让子系统内部的模块功能更容易扩展和维护;
客户端根本不需要知道子系统内部的实现,或者根本不需要知道子系统内部的构成,它只需要跟Facade类交互即可。
有些方法是对系统外的,有些方法是系统内部相互交互的使用的。子系统把那些暴露给外部的功能集中到门面中,这样就可以实现客户端的使用,很好的隐藏了子系统内部的细节。
想让你访问子系统的哪些业务就开通哪些逻辑,不在门面上开通的方法,你休想访问到。
开闭原则
对于扩展是开放的,对于修改是关闭的,这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动。
门面模式刚好相反,子模块扩展的话,需要告知门面,也就是需要改动门面的代码,但是子模块内部扩展或修改就不用,只需要保证对外的接口不发生改变