1. 程式人生 > >結構型:外觀模式及相關應用

結構型:外觀模式及相關應用

文章目錄


外觀(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日誌框架詳解