设计模式学习笔记---------------------------------------------门面模式(外观模式)和日志框架(sl4j、logback、log4j)

   最近在使用日志框架的过程中,有了一些疑惑,这么日志框架,我们如何选择,开发过程中,需要用到的框架很多,不同框架的日志框架一般又不相同,那么如何兼容不同的日志框架呢?

    我们知道的日志框架有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为例

  • 首先,通过slf4j-api.jar的Logger log = LoggerFactory.getLogger(xxx);获取日志对象,作为日志的入口,可以通过log.info()等写日志;
  • LoggerFactory类中调用了org.slf4j.impl.StaticLoggerBinder类的getLoggerFactory()方法获取日志工厂ILoggerFactory(slf4j提供的接口)的实现,然后通过日志工厂的实现类的getLogger()方法返回Logger的实现实例,这里的关键就是org.slf4j.impl.StaticLoggerBinder(该类是接口LoggerFactoryBinder的实现类),虽然slf4j-api.jar中的LoggerFactory类使用了这个类,但这个类并不存在于slf4j-api.jar中,所以,只导入了slf4j-api.jar时,编译是会报错的,因为org.slf4j.impl.StaticLoggerBinder根本就不存在;
  • slf4j-log4j12.jar作为适配器,提供了org.slf4j.impl.StaticLoggerBinder类,所以,当我们导入了slf4j-log4j12.jar后,slf4j-api.jar会直接调用slf4j-log4j12.jar中的org.slf4j.impl.StaticLoggerBinder类。logback日志框架实现了slf4j,所以不需要额外的适配器就能与slf4j结合,logback-classic.jar里有org.slf4j.impl.StaticLoggerBinder类,所以,如果同时导入logback-classic.jar和slf4j-log4j12.jar会报错,因为不能确定使用哪个org.slf4j.impl.StaticLoggerBinder类;
  • 具体的写日志操作,由适配器调用log4j.jar来完成

让我们来写个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文件,都无需改动。

     门面模式刚好相反,子模块扩展的话,需要告知门面,也就是需要改动门面的代码,但是子模块内部扩展或修改就不用,只需要保证对外的接口不发生改变
 

猜你喜欢

转载自blog.csdn.net/qq_33543634/article/details/86714004
今日推荐