結構型:外觀模式及相關應用
阿新 • • 發佈:2018-11-17
文章目錄
外觀(Facade)
外觀模式又叫門面模式,提供了一個統一的介面,用來訪問子系統中的一群介面。
外觀模式定義了一個高層介面,讓子系統更容易使用。
適用場景:
- 子系統越來越複雜,增加外觀模式提供簡單呼叫介面
- 構建多層系統介面,利用外觀物件作為每層的入口,簡化層間呼叫
優缺點
優點:簡化了呼叫過程,無需瞭解子系統,防止帶來風險;減少系統依賴、鬆散耦合;更好的劃分訪問層次;符合迪米特法則,即最少知道原則。
缺點:增加子系統、擴充套件子系統行為容易引入風險,不符合開閉原則。
應用場景
我們考慮一個用積分兌換禮物的場景,積分兌換禮物需要校驗積分是否符合資格、扣減積分以及對接物流系統三個模組,這三個模組也可以理解為三個子系統。
校驗資格子系統:
public class QualifyService {
public boolean isAvailable(PointsGift pointsGift){
System.out.println("校驗" + pointsGift.getName() + " 積分資格通過,庫存通過");
return true;
}
}
扣減積分子系統:
public class PointsPaymentService {
public boolean pay(PointsGift pointsGift){
//扣減積分
System.out.println("支付" + pointsGift.getName() + " 積分成功");
return true;
}
}
對接物流系統的子系統:
public class ShippingService {
public String shipGift(PointsGift pointsGift) {
//物流系統的對接邏輯
System.out.println(pointsGift.getName() + "進入物流系統");
String shippingOrderNo = "666";
return shippingOrderNo;
}
}
積分禮物類:
public class PointsGift {
private String name;
public PointsGift(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
外觀類:
public class GiftExchangeService {
private QualifyService qualifyService = new QualifyService();
private PointsPaymentService pointsPaymentService = new PointsPaymentService();
private ShippingService shippingService = new ShippingService();
public QualifyService getQualifyService() {
return qualifyService;
}
public void setQualifyService(QualifyService qualifyService) {
this.qualifyService = qualifyService;
}
public PointsPaymentService getPointsPaymentService() {
return pointsPaymentService;
}
public void setPointsPaymentService(PointsPaymentService pointsPaymentService) {
this.pointsPaymentService = pointsPaymentService;
}
public ShippingService getShippingService() {
return shippingService;
}
public void setShippingService(ShippingService shippingService) {
this.shippingService = shippingService;
}
public void giftExchange(PointsGift pointsGift){
if(qualifyService.isAvailable(pointsGift)){
//資格校驗通過
if(pointsPaymentService.pay(pointsGift)){
//如果支付積分成功
String shippingOrderNo = shippingService.shipGift(pointsGift);
System.out.println("物流系統下單成功,訂單號是:"+shippingOrderNo);
}
}
}
}
客戶端類:
public class Test {
public static void main(String[] args) {
PointsGift pointsGift = new PointsGift("衣服");
GiftExchangeService giftExchangeService = new GiftExchangeService();
giftExchangeService.giftExchange(pointsGift);
}
}
輸出:
校驗衣服 積分資格通過,庫存通過
支付衣服 積分成功
衣服進入物流系統
物流系統下單成功,訂單號是:666
客戶端建立一個衣服作為積分商品,然後使用積分兌換系統來完成積分兌換,這個積分兌換系統作為一個外觀類整合了各個子系統,而客戶端無需知道具體的子系統。
Spring中的應用
檢視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);
}
// Perform was-null check if demanded (for results that the
// JDBC driver returns as primitives).
if (wasNullCheck && value != null && rs.wasNull()) {
value = null;
}
return value;
}
可以看出,該工具類主要是對jdbc的封裝,向外提供一個隱藏了具體實現細節的介面,對訪問遮蔽複雜的子系統呼叫。
SLF4J中的應用
SLF4J是簡單的日誌外觀模式框架,抽象了各種日誌框架例如Logback、Log4j、Commons-logging和JDK自帶的logging實現介面。它使得使用者可以在部署時使用自己想要的日誌框架。
SLF4J沒有替代任何日誌框架,它僅僅是標準日誌框架的外觀模式。如果在類路徑下除了SLF4J再沒有任何日誌框架,那麼預設狀態是在控制檯輸出日誌。
參考資料
- 弗里曼. Head First 設計模式 [M]. 中國電力出版社, 2007.
- 慕課網java設計模式精講 Debug 方式+記憶體分析
- SLF4J和Logback日誌框架詳解