外观模式

定义:

       它是一种通过为多个复杂的子系统提供一个一致的接口,而使得这些子系统更加容易被访问的设计模式。

优点:

       1、降低了子系统与客户端之间的耦合性,使得子系统的变化不会影响调用它的客户类。(“迪米特法则”的典型应用)

       2、对客户屏蔽了子系统组件减少了客户处理的对象数目,并使得子系统使用起来更加容易。

       3、降低了大型软件系统中的编译依赖性简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。

缺点:

       1、不能很好的限制客户使用子系统类。

       2、增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”

外观模式的结构:

       1、外观(Facade)角色:为多个子系统对外提供一个共同的接口。

       2、子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色来访问它。

       3、客户(Client)角色:通过一个外观角色访问各个系统的功能。

示例:

       每个人都需要吃饭,你可以自己买菜做饭,也可以去饭店吃饭。要是自己买菜做饭,想吃宫保鸡丁、糖醋里脊、地三鲜,那么你就需要买这两个菜所需要的食材,然后到家里自己炒。如果你比较懒,或者不会做饭,那么你就需要去饭店吃饭(其实去饭店吃饭完全可以看成一个外观模式),你只需要告诉漂亮的女服务员你想吃宫保鸡丁、糖醋里脊、地三鲜,那么你就可以在座位上等着了,完全不用操心谁去买原材料、谁去做菜,只等着吃就行了。

       子系统角色1代码如下:

/*子角色系统1,可以做宫保鸡丁*/
public class SubSystem_GongBaoJiDing {

	public void method1() {
		System.out.println("我是厨师1号,我需要做宫保鸡丁!");
	}
}

       子系统角色2代码如下:

/*子角色系统2,可以做糖醋里脊*/
public class SubSystem_TangCuLiJi {

	public void method2() {
		System.out.println("我是厨师2号,我需要做糖醋里脊");
	}
}

       子系统角色3代码如下:

/*子角色系统3,可以做地三鲜*/
public class SubSystem_DiSanXian {

	public void method3() {
		System.out.println("我是厨师3号,我需要做地三鲜");
	}
}

       外观角色代码如下:

/*外观角色,吃什么告诉服务员就可以了*/
public class Facade_FuWuYuan {

	private SubSystem_GongBaoJiDing gongBaoJiDing = new SubSystem_GongBaoJiDing();
	private SubSystem_TangCuLiJi tangCuLiJi= new SubSystem_TangCuLiJi();
	private SubSystem_DiSanXian DiSanXian = new SubSystem_DiSanXian();
	
	public void method() {
		gongBaoJiDing.method1();
		tangCuLiJi.method2();
		DiSanXian.method3();
	}
}

       客户角色代码如下:

/*客户角色*/
public class Client {

	public static void main(String[] args) {
		Facade_FuWuYuan fuWuYuan = new Facade_FuWuYuan();		
		fuWuYuan.method();
	}
}

       程序运行结果如下:

外观模式的结构图:

       如图2所示,这个基本上就是外观模式的结构图了,接下来用精简的代码来简单实现下:

/*子系统1*/
public class SubSystem_01 {

	public void method1() {
		System.out.println("method1");
	}
}
/*子系统2*/
public class SubSystem_02 {

	public void method2() {
		System.out.println("method2");
	}
}
/*子系统3*/
public class SubSystem_03 {

	public void method3() {
		System.out.println("method3");
	}
}
/*外观角色*/
public class Facade {

	private SubSystem_01 subSystem_01 = new SubSystem_01();
	private SubSystem_02 subSystem_02 = new SubSystem_02();
	private SubSystem_03 subSystem_03 = new SubSystem_03();
	
	public void method() {
		subSystem_01.method1();
		subSystem_02.method2();
		subSystem_03.method3();
	}
}
/*客户端角色*/
public class Client {

	public static void main(String[] args) {
		Facade facade = new Facade();
		facade.method();
	}
}

       输入结果如下:

源码分析外观模式的典型应用:

       1、spring jdbc中的外观模式

查看 org.springframework.jdbc.support.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) {
                // 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;
    }
    // ...省略...
}    

        2、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;
    }
    // ...省略...
}

       3、tomcat中的外观模式

Tomcat源码中使用了很多的外观模式

       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);
    }
    // ...省略...
}    

       4、SLF4J ​​​​​​中的外观模式

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

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

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

       SLF4Jhelloworld 如下:

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-api.jarslf4j-api.jar 再根据所绑定的日志处理框架调用不同的 jar 包进行处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐的小三菊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值