博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从源码角度理解Java设计模式——门面模式
阅读量:6487 次
发布时间:2019-06-23

本文共 4851 字,大约阅读时间需要 16 分钟。

hot3.png

一、门面模式介绍

门面模式定义:也叫外观模式,定义了一个访问子系统的接口,除了这个接口以外,不允许其他访问子系统的行为发生。

适用场景:子系统很复杂时,增加一个接口供外部访问。

优点:简化层级间的调用,减少依赖,防止风险。

缺点:如果设计不当,增加新的子系统可能需要修改门面类的源代码,违背了开闭原则。

类型:结构型。

类图:

df2f82f63d20a6bf9e0ea73367abfc7f3d5.jpg

二、门面模式简单实例

小明想开一个餐馆,要去政府部门办理卫生许可证、办理税务登记和办理工商登记,以前小明要一一亲自去办理,这不是一件容易的事。

政府最近简化了政务办理流程,只用小明访问一次简化政务办理的门面就可以办理到全部证件。

interface Executive{	void approve();}class HealthOffice implements Executive{    @Override    public void approve() {        System.out.println("卫生局通过审批");    }  }class RevenueOffice implements Executive{    @Override    public void approve() {        System.out.println("税务局完成登记");    }  }class SaicOffice implements Executive{    @Override    public void approve() {        System.out.println("工商局办理营业执照");    } }//简化政务办理流程的门面class ApproveFacade {    public void wholeApprove() {        new HealthOffice().approve();        new RevenueOffice().approve();        new SaicOffice().approve();    }}

测试一下:

public class FacadeTest {    public static void main(String[] args) {        ApproveFacade af = new ApproveFacade();        //一次调用门面,全部办理        af.wholeApprove();    }}

三、日志门面

阿里巴巴开发手册中有这样一条规定:

581c0154274806f06c479b4067043b0dbc5.jpg

其中Log4j、Logback都是日志框架,它们都有着自己的独立的Api接口。如果单独使用某个框架,会大大增加系统的耦合性。而SLF4J并不是真正的日志框架,它有一套通用的API接口。

所以阿里开发手册中直接强制用SLF4J日志门面,日志门面是门面模式的一个典型应用。SLF4J的helloworld如下:

import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class HelloWorld {  public static void main(String[] args) {    Logger logger = LoggerFactory.getLogger(HelloWorld.class);    logger.info("Hello World");  }}

进入info方法:

2b718bdfb55eeabd543ac572edb6adaf863.jpg

上图的SubstituteLogger.class里还是调用Logger接口的info方法,NOPLogger如同它的名字一样:什么都不做,所以只有在系统引入Logback这个日志框架时,才有了Logger真正的实现类。那Log4j、Logback等日志框架是怎么和SLF4J对接的?

任何日志框架,一定都是通过自己的StaticLoggerBinder类来和SLF4J对接:

package org.slf4j.implimport org.slf4j.ILoggerFactory;public class StaticLoggerBinder {   private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();    //单利模式  public static final StaticLoggerBinder getSingleton() {    return SINGLETON;  }    //声明这个实现编译的SLF4J API的版本,这个字段的值通常随每个版本而修改  public static String REQUESTED_API_VERSION = "1.6.99";  // !final    private StaticLoggerBinder() {    throw new UnsupportedOperationException("This code should have never made it into slf4j-api.jar");  }  public ILoggerFactory getLoggerFactory() {    throw new UnsupportedOperationException("This code should never make it into slf4j-api.jar");  }  public String getLoggerFactoryClassStr() {    throw new UnsupportedOperationException("This code should never make it into slf4j-api.jar");  }}

这个类的实现,在不同的框架中,实现不同,以Logback为例:

a176e9ec925c6d958b789eca66e0d31e5ca.jpg

这个实现的类被设计为或者简单的返回一个默认的LoggerContext(LoggerContext是ILoggerFactory在logback中的实现),或者通过ContextSelector(logback特有的)来选择一个LoggerContext并返回。 

四、源码中的门面模式

3.1 Spring JDBC中的JdbcUtils对原生的JDBC进行封装,让调用者统一访问。

public abstract class JdbcUtils {    //公共关闭链接    public static void closeConnection(Connection con) {        if (con != null) {            try {                con.close();            }            catch (SQLException ex) {                logger.debug("Could not close JDBC Connection", ex);            }            catch (Throwable ex) {                // 没有依赖jdbc驱动时,抛出异常                logger.debug("Unexpected exception on closing JDBC Connection", ex);            }        }    }    //获取结果集的某一条数据    public static Object getResultSetValue(ResultSet rs, int index) throws SQLException {		Object obj = rs.getObject(index);		String className = null;		if (obj != null) {			className = obj.getClass().getName();		}		if (obj instanceof Blob) {			obj = rs.getBytes(index);		}		else if (obj instanceof Clob) {			obj = rs.getString(index);		}		else if (className != null &&				("oracle.sql.TIMESTAMP".equals(className) ||				"oracle.sql.TIMESTAMPTZ".equals(className))) {			obj = rs.getTimestamp(index);		}		else if (className != null && className.startsWith("oracle.sql.DATE")) {			String metaDataClassName = rs.getMetaData().getColumnClassName(index);			if ("java.sql.Timestamp".equals(metaDataClassName) ||					"oracle.sql.TIMESTAMP".equals(metaDataClassName)) {				obj = rs.getTimestamp(index);			}			else {				obj = rs.getDate(index);			}		}		else if (obj != null && obj instanceof java.sql.Date) {			if ("java.sql.Timestamp".equals(rs.getMetaData().getColumnClassName(index))) {				obj = rs.getTimestamp(index);			}		}		return obj;	}   //省略...}

3.2 Tomcat 中大量使用了门面模式。

Tomcat 中有很多不同组件,每个组件要相互交互数据,用门面模式隔离数据是个很好的方法。在Tomcat源码中搜索Facade(门面):

141562057456c3567c181a5883a013618c9.jpg

其中拿RequestFacade.class来说,它是HttpServletRequest外观类,里面封装了各种操作request的常见方法,比如getParameter方法等。

Request.class中封装了 HttpRequest 接口能够提供的数据,是子系统的门面。实际项目中对request进行操作的时候,其实使用的都是RequestFacade这个外观类对象:

protected RequestFacade facade = null;public HttpServletRequest getRequest() {    if (facade == null) {        facade = new RequestFacade(this);    }    return facade;}

门面模式是一个很好的封装方法,一个子系统比较复杂时,比如算法或者业务比较复杂,就可以封装出一个或多个门面出来,项目的结构简单,而且扩展性非常好。

门面模式提供了外界对子系统的访问黑箱操作,无论内部怎么变化,对外部访问者来说,还是同一个门面,同一个方法。

参考:

转载于:https://my.oschina.net/liughDevelop/blog/2988245

你可能感兴趣的文章
caffe solver
查看>>
Rhel6-heartbeat+lvs配置文档
查看>>
0317复利计算的回顾与总结
查看>>
函数对象
查看>>
最全最新个税计算公式---今天你税了吗?
查看>>
linux shell 正则表达式(BREs,EREs,PREs)差异比较(转,当作资料查)
查看>>
二分法求平方根(Python实现)
查看>>
使用startActivityForResult方法(转)
查看>>
so在genymotation中错误问题
查看>>
Visual Studio 原生开发的10个调试技巧(二)
查看>>
Windows内核再次出现0Day漏洞 影响win2000到win10所有版本 反病毒软件恐成瞎子
查看>>
H3C品牌刀片系统强势首发
查看>>
【CSS系列】图像映射
查看>>
First blood
查看>>
删除恢复Hadoop集群中的DataNode
查看>>
Silverlight 2动态创建矩形对象(附完整源代码)
查看>>
从京东技术演进看互联网企业的成长历程
查看>>
MFC ado+mysql+odbc技术分享
查看>>
js中让字符串中特定字符红色显示
查看>>
redhat Nginx 安装
查看>>