设计模式八之外观模式
软件系统中,当一个系统的功能越来越强,子系统会越来越多,客户端对系统的访问也会变得越来越复杂。这时如果系统内部发生改变,客户端也要跟着改变,这违背了“开闭原则”,也违背了“迪米特法则”,所以有必要为多个子系统提供一个统一的接口,从而降低系统的耦合度,这就是外观模式的目标。
1. 模式的定义与特点
1.1 模式的定义
外观模式(Facade):为多个复杂的子系统提供一个对外的接口,使这些子系统更加容易的被访问。该模式对外有一个统一的接口,外部应用不用关心子系统内部的细节,大大降低了应用程序的复杂度,提高了可维护性。
1.2 模式的特点
外观模式是“迪米特法则(最少知道原则)“的典型应用,其优点为:
1. 简化了调用过程,应用无需深入了解子系统;
2. 减少系统依赖,松散耦合;
3. 更好的划分访问层次;
4. 符合迪米特法则,即最少知道原则。
外观模式的缺点有:
1. 增加子系统,扩展子系统行为容易引入风险;
2. 不符合开闭原则。
1.3 模式的使用场景
1. 子系统越来越复杂,增加外观模式提供简单调用接口;
2. 构建多层系统结构,利用外观对象作为每层的入口,简化层级调用。
2. 模式的结构与实现
2.1 模式的结构
外观模式的结构比较简单,主要定义了一个高层接口,它包含了对多个子系统的引用,客户端可以通过它来访问各个子系统的功能。外观模式包含以下主要角色:
1. 外观角色(Facade):为多个子系统对外提供一个共同的接口;
2. 子系统角色(Sub System):实现系统的部分功能,客户端可以通过外观角色访问它;
3. 客户端角色(Client):通过外观角色访问各个子系统的功能。
2.2 模式的实现
/**
* 礼品类
*/
public class Gift {
private String name;
public Gift(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
子系统角色
/**
* 积分服务
*/
public class CoinService {
/**
* 检查积分是否足够兑换
* @param gift
* @return
*/
public boolean isCoinEnough(Gift gift) {
System.out.println("兑换" + gift.getName() + ",积分扣减成功");
return true;
}
}
/**
* 库存服务
*/
public class InventoryService {
/**
* 扣减库存
* @param gift
* @return
*/
public boolean deductInventory(Gift gift) {
System.out.println("扣减" + gift.getName() + "库存成功");
return true;
}
}
/**
* 物流服务
*/
public class LogisticsService {
/**
* 获取物流单号
* @param gift
* @return
*/
public String getLogisticsNo(Gift gift) {
System.out.println(gift.getName() + "进入物流系统");
String logisticsNo = "555";
return logisticsNo;
}
}
外观角色
/**
* 外观角色 - 礼品兑换服务
*/
public class GiftExchangeService {
private CoinService coinService = new CoinService();
private InventoryService inventoryService = new InventoryService();
private LogisticsService logisticsService = new LogisticsService();
public void giftExchange(Gift gift) {
if (coinService.isCoinEnough(gift)) {
if (inventoryService.deductInventory(gift)) {
String logisticsNo = logisticsService.getLogisticsNo(gift);
System.out.println("物流系统下单成功,物流单号为:" + logisticsNo);
}
}
}
}
客户端
public class Client {
public static void main(String[] args) {
Gift gift = new Gift("航空母舰");
GiftExchangeService giftExchangeService = new GiftExchangeService();
giftExchangeService.giftExchange(gift);
}
}
# 运行结果如下:
兑换航空母舰,积分扣减成功
扣减航空母舰库存成功
航空母舰进入物流系统
物流系统下单成功,物流单号为:555
3. 模式在开源软件中的应
3.1 org.springframework.jdbc.support.JdbcUtils 类
public abstract class JdbcUtils {
public static final int TYPE_UNKNOWN = -2147483648;
private static final Log logger = LogFactory.getLog(JdbcUtils.class);
public JdbcUtils() {
}
public static void closeConnection(Connection con) {
if (con != null) {
try {
con.close();
} catch (SQLException var2) {
logger.debug("Could not close JDBC Connection", var2);
} catch (Throwable var3) {
logger.debug("Unexpected exception on closing JDBC Connection", var3);
}
}
}
public static void closeStatement(Statement stmt) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException var2) {
logger.trace("Could not close JDBC Statement", var2);
} catch (Throwable var3) {
logger.trace("Unexpected exception on closing JDBC Statement", var3);
}
}
}
public static void closeResultSet(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException var2) {
logger.trace("Could not close JDBC ResultSet", var2);
} catch (Throwable var3) {
logger.trace("Unexpected exception on closing JDBC ResultSet", var3);
}
}
}
}
Spring JdbcUtils 提供了基于 java.sql 的统一公共接口,外部客户端通过该外观服务访问 java.sql 中的功能实现。