外观模式

外观模式UML类图

外观模式UML类图

外观模式java实现

/**
* DwarvenMineWorker is one of the goldmine subsystems.
DwarvenMineWorker(矮人矿工)是金矿子系统之一。
*/
public abstract class DwarvenMineWorker {

private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenMineWorker.class);

public void goToSleep() {
LOGGER.info("{} goes to sleep.", name());
}

public void wakeUp() {
LOGGER.info("{} wakes up.", name());
}

public void goHome() {
LOGGER.info("{} goes home.", name());
}

public void goToMine() {
LOGGER.info("{} goes to the mine.", name());
}

private void action(Action action) {
switch (action) {
case GO_TO_SLEEP:
goToSleep();
break;
case WAKE_UP:
wakeUp();
break;
case GO_HOME:
goHome();
break;
case GO_TO_MINE:
goToMine();
break;
case WORK:
work();
break;
default:
LOGGER.info("Undefined action");
break;
}
}

/**
* Perform actions.
*/
public void action(Action... actions) {
Arrays.stream(actions).forEach(this::action);
}

public abstract void work();

public abstract String name();

enum Action {
GO_TO_SLEEP, WAKE_UP, GO_HOME, GO_TO_MINE, WORK
}
}

/**
* DwarvenGoldDigger is one of the goldmine subsystems.
DwarvenGoldDigger(矮人金矿)是金矿子系统之一。
*/
public class DwarvenGoldDigger extends DwarvenMineWorker {

private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenGoldDigger.class);

@Override
public void work() {
//挖金。
LOGGER.info("{} digs for gold.", name());
}

@Override
public String name() {
//矮人淘金者
return "Dwarf gold digger";
}
}

/**
* DwarvenTunnelDigger is one of the goldmine subsystems.
DwarvenTunnelDigger(矮人隧道挖掘者)是金矿子系统之一。
*/
public class DwarvenTunnelDigger extends DwarvenMineWorker {

private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenTunnelDigger.class);

@Override
public void work() {
//创造另一个有希望的隧道
LOGGER.info("{} creates another promising tunnel.", name());
}

@Override
public String name() {
//矮人隧道挖掘机
return "Dwarven tunnel digger";
}
}

/**
* DwarvenCartOperator is one of the goldmine subsystems.
DwarvenCartOperator(矮人推车运营商)是金矿子系统之一。
*/
public class DwarvenCartOperator extends DwarvenMineWorker {

private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenCartOperator.class);

@Override
public void work() {
//将金块从矿山中移出
LOGGER.info("{} moves gold chunks out of the mine.", name());
}

@Override
public String name() {
//矮推车操作员
return "Dwarf cart operator";
}
}

/**
* DwarvenGoldmineFacade provides a single interface through which users can operate the
* subsystems.
*
* <p>This makes the goldmine easier to operate and cuts the dependencies from the goldmine user to
* the subsystems.
DwarvenGoldmineFacade提供了一个界面,用户可以通过该界面来操作子系统。
<p>这使金矿更易于操作,并减少了金矿用户与子系统之间的依赖关系。
*/
public class DwarvenGoldmineFacade {

private final List<DwarvenMineWorker> workers;

/**
* Constructor.
*/
public DwarvenGoldmineFacade() {
workers = List.of(
new DwarvenGoldDigger(),
new DwarvenCartOperator(),
new DwarvenTunnelDigger());
}

public void startNewDay() {
makeActions(workers, DwarvenMineWorker.Action.WAKE_UP, DwarvenMineWorker.Action.GO_TO_MINE);
}

public void digOutGold() {
makeActions(workers, DwarvenMineWorker.Action.WORK);
}

public void endDay() {
makeActions(workers, DwarvenMineWorker.Action.GO_HOME, DwarvenMineWorker.Action.GO_TO_SLEEP);
}

private static void makeActions(
Collection<DwarvenMineWorker> workers,
DwarvenMineWorker.Action... actions
) {
workers.forEach(worker -> worker.action(actions));
}
}

/**
* The Facade design pattern is often used when a system is very complex or difficult to understand
* because the system has a large number of interdependent classes or its source code is
* unavailable. This pattern hides the complexities of the larger system and provides a simpler
* interface to the client. It typically involves a single wrapper class which contains a set of
* members required by client. These members access the system on behalf of the facade client and
* hide the implementation details.
*
* <p>In this example the Facade is ({@link DwarvenGoldmineFacade}) and it provides a simpler
* interface to the goldmine subsystem.
当系统非常复杂或难以理解时,通常会使用Facade设计模式,因为该系统具有大量相互依赖的类,或者其源代码不可用。 这种模式隐藏了较大系统的复杂性,并为客户端提供了更简单的界面。 它通常涉及单个包装器类,其中包含客户端所需的一组成员。 这些成员代表外观客户端访问系统,并隐藏实现细节。
<p>在此示例中,外观为({@link DwarvenGoldmineFacade}),它为金矿子系统提供了更简单的界面。
*/
public class App {

/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
var facade = new DwarvenGoldmineFacade();
facade.startNewDay();
facade.digOutGold();
facade.endDay();
}
}

应用场景

spring jdbc中的外观模式

查看 org.springframework.jdbc.support.JdbcUtils

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) {
// We don't trust the JDBC driver: It might throw RuntimeException or Error.
logger.debug("Unexpected exception on closing JDBC Connection", ex);
}
}
}

public static Object getResultSetValue(ResultSet rs, int index, Class<?> requiredType) throws SQLException {
if (requiredType == null) {
return getResultSetValue(rs, index);
}

Object value = null;
boolean wasNullCheck = false;

// Explicitly extract typed value, as far as possible.
if (String.class.equals(requiredType)) {
value = rs.getString(index);
}
else if (boolean.class.equals(requiredType) || Boolean.class.equals(requiredType)) {
value = rs.getBoolean(index);
wasNullCheck = true;
}
else if (byte.class.equals(requiredType) || Byte.class.equals(requiredType)) {
value = rs.getByte(index);
wasNullCheck = true;
}
else if (short.class.equals(requiredType) || Short.class.equals(requiredType)) {
value = rs.getShort(index);
wasNullCheck = true;
}
else if (int.class.equals(requiredType) || Integer.class.equals(requiredType)) {
value = rs.getInt(index);
wasNullCheck = true;
}
else if (long.class.equals(requiredType) || Long.class.equals(requiredType)) {
value = rs.getLong(index);
wasNullCheck = true;
}
else if (float.class.equals(requiredType) || Float.class.equals(requiredType)) {
value = rs.getFloat(index);
wasNullCheck = true;
}
else if (double.class.equals(requiredType) || Double.class.equals(requiredType) ||
Number.class.equals(requiredType)) {
value = rs.getDouble(index);
wasNullCheck = true;
}
else if (byte[].class.equals(requiredType)) {
value = rs.getBytes(index);
}
else if (java.sql.Date.class.equals(requiredType)) {
value = rs.getDate(index);
}
else if (java.sql.Time.class.equals(requiredType)) {
value = rs.getTime(index);
}
else if (java.sql.Timestamp.class.equals(requiredType) || java.util.Date.class.equals(requiredType)) {
value = rs.getTimestamp(index);
}
else if (BigDecimal.class.equals(requiredType)) {
value = rs.getBigDecimal(index);
}
else if (Blob.class.equals(requiredType)) {
value = rs.getBlob(index);
}
else if (Clob.class.equals(requiredType)) {
value = rs.getClob(index);
}
else {
// Some unknown type desired -> rely on getObject.
value = getResultSetValue(rs, index);
}

if (wasNullCheck && value != null && rs.wasNull()) {
value = null;
}
return value;
}
// ...省略...
}

该工具类主要是对原生的 jdbc 进行了封装

Mybatis中的外观模式

查看 org.apache.ibatis.session.Configuration 类中以 new 开头的方法

public class Configuration {
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor 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 (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}

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) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
// ...省略...
}

该类主要对一些创建对象的操作进行封装

Tomcat 中的外观模式

Tomcat 源码中大量使用了很多外观模式
tomcat-facade
org.apache.catalina.connector.Request 和 org.apache.catalina.connector.RequestFacade 这两个类都实现了 HttpServletRequest 接口
在 Request 中调用 getRequest() 实际获取的是 RequestFacade 的对象

protected RequestFacade facade = null;

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

在 RequestFacade 中再对认为是子系统的操作进行封装

public class RequestFacade implements HttpServletRequest {
/**
* The wrapped request.
*/
protected Request request = null;

@Override
public Object getAttribute(String name) {
if (request == null) {
throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
}
return request.getAttribute(name);
}
// ...省略...
}

SLF4J 中的外观模式

SLF4J 是简单的日志外观模式框架,抽象了各种日志框架例如 Logback、Log4j、Commons-logging 和 JDK 自带的 logging 实现接口。它使得用户可以在部署时使用自己想要的日志框架。

SLF4J 没有替代任何日志框架,它仅仅是标准日志框架的外观模式。如果在类路径下除了 SLF4J 再没有任何日志框架,那么默认状态是在控制台输出日志。

日志处理框架 Logback 是 Log4j 的改进版本,原生支持SLF4J(因为是同一作者开发的),因此 Logback+SLF4J 的组合是日志框架的最佳选择,比 SLF4J+其它日志框架 的组合要快一些。而且Logback的配置可以是XML或Groovy代码。

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

下图为 SLF4J 与日志处理框架的绑定调用关系
slf4j
应用层调用 slf4j-api.jar,slf4j-api.jar 再根据所绑定的日志处理框架调用不同的 jar 包进行处理