定义

  • 又叫门面模式,提供了一个统一的接口,用来访问系统中的一群接口
  • 外观模式定义了一个高层的接口,让子系统更容易使用

类型

结构型

使用场景

  • 子系统越来越复杂,增加外观模式提供简单调用接口
  • 构建多层系统结构,利用外观对象作为每层的入口,简化层间调用

优点

  • 简化了调用过程,无须了解深入子系统,防止带来风险
  • 减少系统依赖、松散耦合
  • 更好的划分访问层次
  • 符合迪米特法则,即最少知道原则

缺点

  • 增加子系统、扩展子系统行为容易引入风险
  • 不符合开闭原则

相关设计模式

  • 外观模式和中介者模式
    本质上的区别,外观模式关注的是外界和子系统的交互,而中介者模式关注的是子系统内部之间的交互

  • 外观模式和单例模式

  • 外观模式和抽象工厂模式
    外观类可以通过抽象工厂获取子系统的实例,这样子系统可以将内部对外观类进行屏蔽

coding

迪米特原则是最少知道原则,外观模式是迪米特法则的非常典型的例子,降低我们应用层client和子系统之间的耦合度。
我们看一下积分兑换礼物的功能代码
积分礼物

/*** 积分礼物**/
public class PointGift {private String name;public PointGift(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

礼物兑换校验子系统

/*** 礼物兑换校验子系统**/
public class QualifyService {public boolean isAvailable(PointGift pointGift){System.out.println("校验"+pointGift.getName()+" 积分资格通过,库存通过");return true;}
}

积分支付的系统

/*** 积分支付的系统**/
public class PointPaymentService {public boolean pay(PointGift pointGift){//扣减积分System.out.println("支付"+pointGift.getName()+" 积分成功");return true;}
}

物流系统

/*** 物流系统**/
public class ShippingService {/*** 返回物流单号* @param pointGift* @return*/public String shipGift(PointGift pointGift){//物流系统的对接逻辑System.out.println(pointGift.getName()+"进入物流系统");String shippingOrderNo="666";return shippingOrderNo;}
}

现在三个子系统礼物兑换校验子系统、积分支付的系统、物流系统都已经有了,我们应用层在对接的时候我要关注这3个子系统,要挨个进行交互,首先交互资格校验、然后支付积分、最后调用物流系统运输礼物。例如说说一旦服务拆分后,作为rpc服务,对外提供这3个接口,要求对接者来实现这3个接口,同时还要保证他们的顺序,包括怎么使用都要写一个说明,那其实对方只关心的是礼物兑换逻辑,这3个服务呢完全可以封装到内部,对外只提供一个外观类,对方不需要知道这么多子系统,包括他们之间什么顺序,里面什么逻辑,这些子系统的逻辑都不需要知道。其实可以想想迪米特法则,对方不需要知道的都不知道,那外观模式可以很好的体现迪米特法则,不该知道的就不知道。
我们现在需要应用层知道什么呢?

礼物兑换的服务

/*** 礼物兑换的服务**/
public class GiftExchangeService {//注入服务private QualifyService qualifyService;private PointPaymentService pointPaymentService;private ShippingService shippingService;//注入public void setQualifyService(QualifyService qualifyService) {this.qualifyService = qualifyService;}public void setPointPaymentService(PointPaymentService pointPaymentService) {this.pointPaymentService = pointPaymentService;}public void setShippingService(ShippingService shippingService) {this.shippingService = shippingService;}/*** 礼物兑换* @param pointGift*/public void giftExchange(PointGift pointGift){if(qualifyService.isAvailable(pointGift)){//资格校验通过if(pointPaymentService.pay(pointGift)){//如果积分支付成功String ShippingOrderNo= shippingService.shipGift(pointGift);System.out.println("物流系统下单成功,订单号是:"+ShippingOrderNo);}}}
}

看下类图

看到一个礼物兑换的外观类对外提供一个giftExchange方法,下面有3个子系统,分别是资格校验、物流、支付,并且是1:1的菱形关系,也即是组合关系,对外只和外观类交互,所有的子系统是不需要关心的,这个图就很好诠释了外观类的UML类图

应用层

public class Test {public static void main(String[] args) {PointGift pointGift=new PointGift("T恤");GiftExchangeService giftExchangeService=new GiftExchangeService();//注入giftExchangeService.setQualifyService(new QualifyService());giftExchangeService.setPointPaymentService(new PointPaymentService());giftExchangeService.setShippingService(new ShippingService());giftExchangeService.giftExchange(pointGift);}
}

运行结果

校验T恤 积分资格通过,库存通过
支付T恤 积分成功
T恤进入物流系统
物流系统下单成功,订单号是:666

运行结果没有问题,再看下现在的类图

看类图,Test是不需要关心子系统的,可是上图中和子系统发生关系了,Test创建了子系统,那实际在使用的时候只要保证Test和外观类这条黄线就是ok的,但是这里面为什么还有线呢?因为在Test里我们通过new来创建了子系统,现在我们更符合实际一点,假设外观类里的子系统已经通过spring注入了

/*** 礼物兑换的服务**/
public class GiftExchangeService {//注入服务private QualifyService qualifyService=new QualifyService();private PointPaymentService pointPaymentService=new PointPaymentService();private ShippingService shippingService=new ShippingService();/*** 礼物兑换* @param pointGift*/public void giftExchange(PointGift pointGift){if(qualifyService.isAvailable(pointGift)){//资格校验通过if(pointPaymentService.pay(pointGift)){//如果积分支付成功String ShippingOrderNo= shippingService.shipGift(pointGift);System.out.println("物流系统下单成功,订单号是:"+ShippingOrderNo);}}}
}

应用层

public class Test {public static void main(String[] args) {PointGift pointGift=new PointGift("T恤");GiftExchangeService giftExchangeService=new GiftExchangeService();giftExchangeService.giftExchange(pointGift);}
}

运行结果肯定还是一样的,我们再看看uml类图

看到Test和子系统的关系已经消失了,子系统只和外观类通信,应用层只和外观类通信。所以我们日常在开发工作的时候,也要注意我们应用层到底和没和子系统发生关系,这里也是检验外观模式使用正确与否的一个因素。
上面的示例中,我们使用的是实体外观类,在UML类图中也看到,它完美的支持了迪米特法则,但是新增一个子系统的话,我们还要修改实体外观类,从这个角度出发,这并不符合开闭原则,再扩展下,如果我们这个外观类使用抽象外观类的话,也就是说我们上面还有个接口外观类,通过这个外观类来实现上面的抽象外观接口,这也是一个扩展的方式。但是具体我们是使用实体外观类还是抽象外观外观类来实现业务场景呢?还是要看具体的业务场景要求,和对它扩展的一个预期,如果这个外观类不需要经常变化,或者以后的业务扩展非常有限的话,我们直接使用实体外观类也是可以的,可以减少复杂度;那如果预期每一两周就要新增一个子系统,那我们就要使用抽象外观类了。

例如说下面uml中有个抽象外观类的实现类,在左边又一个抽象外观类的实现类,如果左边的实现变化比较大的话,再通过应用层Test关联左边的框,即使用新的抽象外观类的实现类,这样右边老的是不需要变化的,对于版本控制,例如说一个app不同的版本,里面的逻辑可能是不一样的,在版本控制上也可以找到使用的场景,打个比方我们可以通过app获取具体使用的版本,然后调用对应版本的外观类实现。

源码解析

spring-jdbc中的JdbcUtils

closeConnection对Connection 做了封装

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) {// We don't trust the JDBC driver: It might throw RuntimeException or Error.logger.debug("Unexpected exception on closing JDBC Connection", ex);}}}
public static void closeStatement(Statement stmt) {if (stmt != null) {try {stmt.close();}catch (SQLException ex) {logger.trace("Could not close JDBC Statement", ex);}catch (Throwable ex) {// We don't trust the JDBC driver: It might throw RuntimeException or Error.logger.trace("Unexpected exception on closing JDBC Statement", ex);}}}
public static void closeResultSet(ResultSet rs) {if (rs != null) {try {rs.close();}catch (SQLException ex) {logger.trace("Could not close JDBC ResultSet", ex);}catch (Throwable ex) {// We don't trust the JDBC driver: It might throw RuntimeException or Error.logger.trace("Unexpected exception on closing JDBC ResultSet", ex);}}}
public static Object getResultSetValue(ResultSet rs, int index) throws SQLException {。。。
public static Object extractDatabaseMetaData(DataSource dataSource, DatabaseMetaDataCallback action)throws MetaDataAccessException {。。。

mybatis中的configuration

public MetaObject newMetaObject(Object object) {return MetaObject.forObject(object, this.objectFactory, this.objectWrapperFactory, this.reflectorFactory);}
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);parameterHandler = (ParameterHandler)this.interceptorChain.pluginAll(parameterHandler);return parameterHandler;}

tomcat源码中RequestFacade

看名字就知道是一个Request的外观类,正是一个HttpServletRequest的外观类

public class RequestFacade implements HttpServletRequest {。。。


里面封装了很多方法,有像我们熟悉的getParameter等等。再来看看Request对象

package org.apache.catalina.connector;public class Requestimplements HttpServletRequest {。。。

那Request和RequestFacade是什么关系呢?它们两个都实现了HttpServletRequest接口,并且Request里有Facade,里面操作都是通过Facade来操作的

 /*** The facade associated with this request.*/protected RequestFacade facade = null;/*** Return the <code>ServletRequest</code> for which this object* is the facade.  This method must be implemented by a subclass.*/public HttpServletRequest getRequest() {if (facade == null) {facade = new RequestFacade(this);}return facade;}

tomcat中的ResponseFacade

跟RequestFacade一样

tomcat中的StandardSessionFacade

是处理Session的外观类

public class StandardSessionFacadeimplements HttpSession {

tomcat还有很多都是使用外观模式的,我们可以搜facade

外观模式又叫门面模式?相关推荐

  1. 6中结构型设计模式的对比理解(Composite组合模式,Proxy代理模式,Flyweight享元模式,Facade门面模式,Bridge桥接模式,Decorator装饰器模式)

    结构型模式 结构型模式用来组装 类和对象,以获得更大的结构. 结构型类模式,通过继承机制来组合接口或类.简单的例子就是多重继承,最后一个类拥有所有父类的性质.这个模式有助于独立开发一个协同类.另一个例 ...

  2. 深入理解设计模式-外观模式(门面模式)

    文章目录 一.什么是外观模式 二.样例分析 三.优缺点 四.使用场景 五.Servlet源码应用解析 结尾 一.什么是外观模式 外观模式也称为门面模式,是一种通过为多个复杂的子系统提供一个一致的接口, ...

  3. JAVA设计模式之(九)门面模式(外观模式)

    本文继续介绍23种设计模式系列之门面模式(外观模式). 医院的例子 现代的软件系统都是比较复杂的,设计师处理复杂系统的一个常见方法便是将其"分而治之",把一个系统划分为几个较小的子 ...

  4. JAVA之门面模式概述

    门面模式(Facade Pattern)也叫做外观模式,是一种比较常用的封装模式,要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行.门面模式提供一个高层次的接口,使得子系统更易于使用. 医 ...

  5. 三分钟学会《门面模式》

    前言 只有光头才能变强 回顾前面所写过的设计模式: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 单例模式你会几种写法? 工厂模式理解了没有? 策略模式原来就这么简单! 无论是面试还是个人的提升 ...

  6. 设计模式之禅【门面模式】

    真刀实枪之门面模式 从投递信件说起 写过纸质信件没,如果你是00后,那机会应该很少,不过没关系,先来看看步骤 写信的内容 写信封 把信放到信封 投递到信箱中进行邮递 看下该过程的类图 趁热打铁,代码跟 ...

  7. 【设计模式】门面模式

    阅读目录 案例分析 门面模式的结构 结合代码分析 门面模式的实现 一个系统可以有几个门面类 为子系统增加新行为 门面模式的优点 门面模式在Tomcat中的使用 在阎宏博士的<JAVA与模式> ...

  8. 设计模式之门面模式详解

    设计模式之门面模式详解 文章目录 设计模式之门面模式详解 一.什么是门面模式 二.门面模式的应用场景 三.门面模式的角色组成 四.门面模式通用写法 五.门面模式在业务中的应用 六.门面模式优缺点 一. ...

  9. [C++实现 设计模式(14)] : 门面模式

    文章目录 情景描述 门面模式的定义 门面模式的应用 门面模式的优点 门面模式的缺点 门面模式的是使用场景 门面模式的注意事项 一个子系统可以有多个门面 门面不参与子系统内的业务逻辑 *参考书籍* : ...

  10. 设计模式系列之「门面模式」

    <三国演义>中有曰:刘备.诸葛亮趁曹操赤壁之战失利,大肆扩充地盘,先后占领荆州大部地区,引起东吴孙权的警惕.为了限制刘备势力的发展,鲁肃奉命向刘备讨还荆州,但遭到拒绝.东吴大都督周瑜向孙权 ...

最新文章

  1. 线段树专辑——pku 2886 Who Gets the Most Candies?
  2. 16:00面试,16:08就出来了 ,问的实在是太...
  3. 跟随美国博导 12 年,我学到最深刻的不是科研,而是……
  4. Windows Server 2012 R2配置ISCSI磁盘共享盘(4)
  5. VMWare下的DOS与宿主机的文件共享
  6. 2013年4月27日星期六
  7. LInux 安全测试 2
  8. Your local changes would be overwritten by merge. Commit, stash or revert them to proceed.
  9. python内置函数大全,赶紧收藏!!
  10. Volatility内存分析工具-某即时通讯软件Windows端数据库密钥的分析
  11. ubuntu14.04 64bit安装android的NDK开发环境
  12. ytkah网站建设解决方案 大中小微企业营销利器
  13. noteexpress如何不生成校对报告
  14. c 语言tcp实现电子词典项目
  15. diyupload插件:批量图片上传
  16. Python | 使用Socks5
  17. 更新至2021各省份上传服务器地址(航信、百旺、税务UK)
  18. LoRa技术-什么是LoRa
  19. docker官方文档翻译5
  20. 特别放松:海盗分金问题

热门文章

  1. 用正则表达式改变或清除页面超链接标签url内容
  2. android sid如何验证有效性,使用RMAN验证备份的有效性
  3. SpringCloud OpenFeign(二)
  4. mysql的架构及查询sql的执行流程(二)
  5. mysql中count(*)、count(1)和count(字段)的区别
  6. Mesos+Marathon docker 集群管理
  7. Android子线程创建Handler方法
  8. ConstraintLayout约束控件详解
  9. python-unicode十进制数字转中文
  10. mongodb 的 GridFS 详细分析(二)