设计模式-门面模式

  • 门面模式(Facade Pattern)

又叫外观模式,提供了一个统一的接口,用来访问子系统中的一群接 口。
其主要特征是定义了一个高层接口,让子系统更容易使用,属于结构性模式。
原文:Provide a unified interface to a set of interfaces in a subsystem.Facade defines a higher-level interface that makes the subsystem easier to use.
解释:要求一个子系统的外部与其内部的通信必须通过一个同一的对象进行。门面模式提供一个高层次的接口,使得 子系统更易于使用。

其实,在我们日常的编码工作中,我们都在有意无意地大量使用门面模式,但凡只要高层模块需要 调度多个子系统(2 个以上类对象),我们都会自觉地创建一个新类封装这些子系统,提供精简接口, 让高层模块可以更加容易间接调用这些子系统的功能。尤其是现阶段各种第三方 SDK,各种开源类库, 很大概率都会使用门面模式。尤其是你觉得调用越方便的,门面模式使用的一般更多。

  • 门面模式的应用场景

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

  • 门面模式的通用写法
    在这里插入图片描述

门面模式主要包含 2 种角色:

外观角色(Facade):也称 门面角色,系统对外的统一接口;

子系统角色(SubSystem):可以同时有一个或多个 SubSystem。每个 SubSytem 都不是一个单独 的类,而是一个类的集合。SubSystem 并不知道 Facade 的存在,对于 SubSystem 而言,Facade 只 是另一个客户端而已(即 Facade 对 SubSystem 透明)。

门面模式通用写法:

public class SubSystemA { public void doA() { System.out.println("doing A stuff"); } }
public class SubSystemB { public void doB() { System.out.println("doing B stuff"); } }
public class SubSystemC { public void doC() { System.out.println("doing C stuff"); } }

外观角色Facade类

public class Facade {
	 private SubSystemA a = new SubSystemA(); 
	 private SubSystemB b = new SubSystemB(); 
	 private SubSystemC c = new SubSystemC(); 
	 // 对外接口
	 public void doA() { this.a.doA(); }
	 // 对外接口 
	 public void doB() { this.b.doB(); }
	 // 对外接口 
	 public void doC() { this.c.doC(); } 
}

客户端调用

public static void main(String[] args) { 
	Facade facade = new Facade(); 
	facade.doA(); 
	facade.doB(); 
	facade.doC(); 
}
  • 门面模式在源码中的应用
  1. Spring JDBC 模块下的 JdbcUtils 类,它封装了和 JDBC 相关的所有操作,内部怎么实现的我们不用管,只需要调用 它的一个方法,就能够实现对应的功能,它一个代码片段如下:
public abstract class JdbcUtils {

	/**
	 * Constant that indicates an unknown (or unspecified) SQL type.
	 * @see java.sql.Types
	 */
	public static final int TYPE_UNKNOWN = Integer.MIN_VALUE;

	private static final Log logger = LogFactory.getLog(JdbcUtils.class);


	/**
	 * Close the given JDBC Connection and ignore any thrown exception.
	 * This is useful for typical finally blocks in manual JDBC code.
	 * @param con the JDBC Connection to close (may be {@code null})
	 */
	public static void closeConnection(@Nullable 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);
			}
		}
	}

	/**
	 * Close the given JDBC Statement and ignore any thrown exception.
	 * This is useful for typical finally blocks in manual JDBC code.
	 * @param stmt the JDBC Statement to close (may be {@code null})
	 */
	public static void closeStatement(@Nullable 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);
			}
		}
	}
	………… 
}

Mybatis的Configuration类(org.apache.ibatis.session.Configuration),它其中有很多 new 开头的方法,来看一下源代码:

 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;
    }

    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
        ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
        ResultSetHandler resultSetHandler = (ResultSetHandler)this.interceptorChain.pluginAll(resultSetHandler);
        return resultSetHandler;
    }

    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
        return statementHandler;
    }

    public Executor newExecutor(Transaction transaction) {
        return this.newExecutor(transaction, this.defaultExecutorType);
    }

    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? this.defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Object executor;
        if (ExecutorType.BATCH == executorType) {
            executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
            executor = new ReuseExecutor(this, transaction);
        } else {
            executor = new SimpleExecutor(this, transaction);
        }

        if (this.cacheEnabled) {
            executor = new CachingExecutor((Executor)executor);
        }

        Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
        return executor;
    }

上面的这些方法都是对 JDBC 中关键组件操作的封装。
(在上面 executor = new CachingExecutor((Executor)executor); 中,可以看到,这里使用了包装器模式)

  • 门面模式的优缺点

优点:
1、简化了调用过程,无需深入了解子系统,以防给子系统带来风险。
2、减少系统依赖、松散耦合 8
3、更好地划分访问层次,提高了安全性
4、遵循迪米特法则,即最少知道原则。
缺点:
1、当增加子系统和扩展子系统行为时,可能容易带来未知风险
2、不符合开闭原则
3、某些情况下可能违背单一职责原则

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值