前言
本文的主要內容:java
介紹外觀模式spring
示例sql
本身泡茶apache
到茶館喝茶設計模式
外觀模式總結api
外觀模式的典型應用微信
spring JDBC 中的外觀模式session
Mybatis中的外觀模式app
Tomcat 中的外觀模式框架
SLF4J 中的外觀模式
外觀模式
外觀模式是一種使用頻率很是高的結構型設計模式,它經過引入一個外觀角色來簡化客戶端與子系統之間的交互,爲複雜的子系統調用提供一個統一的入口,下降子系統與客戶端的耦合度,且客戶端調用很是方便。
外觀模式又稱爲門面模式,它是一種對象結構型模式。外觀模式是迪米特法則的一種具體實現,經過引入一個新的外觀角色能夠下降原有系統的複雜度,同時下降客戶類與子系統的耦合度。
外觀模式包含以下兩個角色:
Facade(外觀角色):在客戶端能夠調用它的方法,在外觀角色中能夠知道相關的(一個或者多個)子系統的功能和責任;在正常狀況下,它將全部從客戶端發來的請求委派到相應的子系統去,傳遞給相應的子系統對象處理。
SubSystem(子系統角色):在軟件系統中能夠有一個或者多個子系統角色,每個子系統能夠不是一個單獨的類,而是一個類的集合,它實現子系統的功能;每個子系統均可以被客戶端直接調用,或者被外觀角色調用,它處理由外觀類傳過來的請求;子系統並不知道外觀的存在,對於子系統而言,外觀角色僅僅是另一個客戶端而已。
外觀模式的目的不是給予子系統添加新的功能接口,而是爲了讓外部減小與子系統內多個模塊的交互,鬆散耦合,從而讓外部可以更簡單地使用子系統。
外觀模式的本質是:封裝交互,簡化調用。
示例
泡茶須要水 Water
public class Water {
private int temperature; // 溫度
private int capacity; // 容量
public Water() {
this.temperature = 0;
this.capacity = 10;
}
// 省略...
}
泡茶須要茶葉 TeaLeaf
public class TeaLeaf {
private String teaName;
// 省略...
}
燒水須要用水壺燒,將水加熱
public class KettleService {
public void waterBurning(String who, Water water, int burnTime) {
// 燒水,計算最終溫度
int finalTermperature = Math.min(100, water.getTemperature() + burnTime * 20);
water.setTemperature(finalTermperature);
System.out.println(who + " 使用水壺燒水,最終水溫爲 " + finalTermperature);
}
}
泡茶,將燒好的水與茶葉進行沖泡,最終獲得一杯茶水
public class TeasetService {
public Teawater makeTeaWater(String who, Water water, TeaLeaf teaLeaf) {
String teawater = "一杯容量爲 " + water.getCapacity() + ", 溫度爲 " + water.getTemperature() + " 的" + teaLeaf.getTeaName() + "茶水";
System.out.println(who + " 泡了" + teawater);
return new Teawater(teawater);
}
}
人喝茶水
public class Man {
private String name;
public Man(String name) {
this.name = name;
}
public void drink(Teawater teawater) {
System.out.println(name + " 喝了" + teawater.getTeaWater());
}
}
本身泡茶喝
張3、李四各自泡茶喝,各自都須要準備茶具、茶葉、水,各自還要完成燒水、泡茶等操做
public class Main {
public static void main(String[] args) {
Man zhangsan = new Man("張三");
KettleService kettleService1 = new KettleService();
TeasetService teasetService1 = new TeasetService();
Water water1 = new Water();
TeaLeaf teaLeaf1 = new TeaLeaf("西湖龍井");
kettleService1.waterBurning(zhangsan.getName(), water1, 4);
Teawater teawater1 = teasetService1.makeTeaWater(zhangsan.getName(), water1, teaLeaf1);
zhangsan.drink(teawater1);
System.out.println();
Man lisi = new Man("李四");
KettleService kettleService2 = new KettleService();
TeasetService teasetService2 = new TeasetService();
Water water2 = new Water(10, 15);
TeaLeaf teaLeaf2 = new TeaLeaf("碧螺春");
kettleService2.waterBurning(lisi.getName(), water2, 4);
Teawater teawater2 = teasetService2.makeTeaWater(lisi.getName(), water2, teaLeaf2);
lisi.drink(teawater2);
}
}
輸出爲
張三 使用水壺燒水,最終水溫爲 80
張三 泡了一杯容量爲 10, 溫度爲 80 的西湖龍井茶水
張三 喝了一杯容量爲 10, 溫度爲 80 的西湖龍井茶水
李四 使用水壺燒水,最終水溫爲 90
李四 泡了一杯容量爲 15, 溫度爲 90 的碧螺春茶水
李四 喝了一杯容量爲 15, 溫度爲 90 的碧螺春茶水
本身泡茶喝模式圖
![本身泡茶喝模式圖](http://static.javashuo.com/static/loading.gif)
到茶館喝茶
茶館,茶館有不一樣的套餐
public class TeaHouseFacade {
private String name;
private TeasetService teasetService;
private KettleService kettleService;
public TeaHouseFacade(String name) {
this.name = name;
this.teasetService = new TeasetService();
this.kettleService = new KettleService();
}
public Teawater makeTea(int teaNumber) {
switch (teaNumber) {
case 1:
Water water1 = new Water();
TeaLeaf teaLeaf1 = new TeaLeaf("西湖龍井");
kettleService.waterBurning(this.name, water1, 4);
Teawater teawater1 = teasetService.makeTeaWater(this.name, water1, teaLeaf1);
return teawater1;
case 2:
Water water2 = new Water(10, 15);
TeaLeaf teaLeaf2 = new TeaLeaf("碧螺春");
kettleService.waterBurning(this.name, water2, 4);
Teawater teawater2 = teasetService.makeTeaWater(this.name, water2, teaLeaf2);
return teawater2;
default:
Water water3 = new Water();
TeaLeaf teaLeaf3 = new TeaLeaf("招牌烏龍");
kettleService.waterBurning(this.name, water3, 5);
Teawater teawater3 = teasetService.makeTeaWater(this.name, water3, teaLeaf3);
return teawater3;
}
}
}
張三和李四點茶,只須要告訴茶館套餐編號便可,水、茶葉由茶館準備,燒水泡茶的操做由茶館統一完成
public class Test {
public static void main(String[] args) {
TeaHouseFacade teaHouseFacade = new TeaHouseFacade("老舍茶館");
Man zhangsan = new Man("張三");
Teawater teawater = teaHouseFacade.makeTea(1);
zhangsan.drink(teawater);
System.out.println();
Man lisi = new Man("李四");
Teawater teawater1 = teaHouseFacade.makeTea(2);
lisi.drink(teawater1);
}
}
輸出爲
老舍茶館 使用水壺燒水,最終水溫爲 80
老舍茶館 泡了一杯容量爲 10, 溫度爲 80 的西湖龍井茶水
張三 喝了一杯容量爲 10, 溫度爲 80 的西湖龍井茶水
老舍茶館 使用水壺燒水,最終水溫爲 90
老舍茶館 泡了一杯容量爲 15, 溫度爲 90 的碧螺春茶水
李四 喝了一杯容量爲 15, 溫度爲 90 的碧螺春茶水
到茶館喝茶模式圖
![到茶館喝茶模式圖](http://static.javashuo.com/static/loading.gif)
外觀模式總結
外觀模式的主要優勢以下:
它對客戶端屏蔽了子系統組件,減小了客戶端所需處理的對象數目,並使得子系統使用起來更加容易。經過引入外觀模式,客戶端代碼將變得很簡單,與之關聯的對象也不多。
它實現了子系統與客戶端之間的鬆耦合關係,這使得子系統的變化不會影響到調用它的客戶端,只須要調整外觀類便可。
一個子系統的修改對其餘子系統沒有任何影響,並且子系統內部變化也不會影響到外觀對象。
外觀模式的主要缺點以下:
不能很好地限制客戶端直接使用子系統類,若是對客戶端訪問子系統類作太多的限制則減小了可變性和靈活性。
若是設計不當,增長新的子系統可能須要修改外觀類的源代碼,違背了開閉原則。
適用場景:
當要爲訪問一系列複雜的子系統提供一個簡單入口時能夠使用外觀模式。
客戶端程序與多個子系統之間存在很大的依賴性。引入外觀類能夠將子系統與客戶端解耦,從而提升子系統的獨立性和可移植性。
在層次化結構中,能夠使用外觀模式定義系統中每一層的入口,層與層之間不直接產生聯繫,而經過外觀類創建聯繫,下降層之間的耦合度。
源碼分析外觀模式的典型應用
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中的外觀模式](http://static.javashuo.com/static/loading.gif)
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與日誌處理框架的綁定調用關係](http://static.javashuo.com/static/loading.gif)
應用層調用 slf4j-api.jar
,slf4j-api.jar
再根據所綁定的日誌處理框架調用不一樣的 jar 包進行處理
參考:
劉偉:設計模式Java版
慕課網java設計模式精講 Debug 方式+內存分析
Java日誌框架:slf4j做用及其實現原理
推薦閱讀
設計模式 | 簡單工廠模式及典型應用
設計模式 | 工廠方法模式及典型應用
設計模式 | 抽象工廠模式及典型應用
設計模式 | 建造者模式及典型應用
設計模式 | 原型模式及典型應用
![關注【小旋鋒】微信公衆號](http://static.javashuo.com/static/loading.gif)
本文分享自微信公衆號 - 小旋鋒(whirlysBigData)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。