【设计模式与体系结构】结构型模式-外观模式
引言
昨夜见军帖,可汗大点兵,军书十二卷,卷卷有爷名。阿爷无大儿,木兰无长兄,愿为市鞍马,从此替爷征。东市买骏马,西市买鞍鞯,南市买辔头,北市买长鞭。————《木兰诗 节选》
花木兰替父从军,欲买马、鞍鞯、辔头和长鞭,需要分别去往东市、西市、南市和北市。换言之,花木兰一人就得与四个贸易场所有贸易关系才能买到所需装备。被可汗大点兵的,自然不止花木兰一家,需要买马、鞍鞯、辔头和长鞭的,也必然不止花木兰一人,如果每个人都需要去到四个贸易场所并且都发生贸易关系,那么这个贸易关系显然会较为复杂。况且这还是知道去哪里贸易的情况,倘若所需贸易的物品不知所在之处,那贸易的复杂程度就更高了。倘若有一个供销社,能直接买到马、鞍鞯、辔头和长鞭,甚至其他装备,那么每个被点兵且需要买装备的人,只需要去供销社一个贸易场所即可,可以大大降低贸易的复杂度。
想要引入类似于供销社这种处理了各个子贸易场所的贸易关系的角色,这就要引入了结构型设计模式中的外观模式。
简介
外观模式(Facade Pattern)为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这个子系统更加容易被使用。
外观模式的角色
- 外观角色:这是外观模式的核心。它被客户角色调用,因此它熟悉子系统的功能。其内部根据客户角色已有的需求预定了集中功能组合
- 子系统角色:实现子系统的功能。它和客户角色相互独立,彼此不知道彼此的任何信息
- 客户角色:调用外观角色得到想要的功能
外观模式的优点
- 降低客户角色与子系统角色的耦合度
- 对客户屏蔽了子系统,减少了客户角色需要处理的对象数目,并且使得子系统用起来更加容易
外观模式的缺点
- 不符合开闭原则,若子系统发生变化,则外观模式可能也需要做出修改
正文
每个贸易场所都相当于一个子系统,定义一个子系统共有的贸易接口 Saleable.java
Saleable
public interface Saleable { void sell(); }
定义一个骏马贸易场所 HorseShop.java
HorseShop
public class HorseShop implements Saleable { @Override public void sell() { System.out.println("买骏马"); } }
定义一个鞍鞯贸易场所 SaddleShop.java
SaddleShop
public class SaddleShop implements Saleable { @Override public void sell() { System.out.println("买鞍鞯"); } }
定义一个辔头贸易场所 BriddleShop.java
BriddleShop
public class BridleShop implements Saleable { @Override public void sell() { System.out.println("买辔头"); } }
定义一个长鞭贸易场所 WhipShop.java
WhipShop
public class WhipShop implements Saleable { @Override public void sell() { System.out.println("买长鞭"); } }
定义一个客户类 Client.java
Client
public class Client { public static void main(String[] args) { func1(); func2(); } private static void func1() { HorseShop horseShop = new HorseShop(); horseShop.sell(); SaddleShop saddleShop = new SaddleShop(); saddleShop.sell(); BridleShop bridleShop = new BridleShop(); bridleShop.sell(); WhipShop whipShop = new WhipShop(); whipShop.sell(); } private static void func2() { SaddleShop saddleShop = new SaddleShop(); saddleShop.sell(); HorseShop horseShop = new HorseShop(); horseShop.sell(); WhipShop whipShop = new WhipShop(); whipShop.sell(); System.out.println("买口粮"); } }
从上述客户类代码可以看出,在不引入外观角色的情况下,客户类需要与各个子系统有着相对复杂的直接交互情况,客户类需要处理较多的对象。不妨再引入一个外观角色 ShopFacade.java
ShopFacade
public class ShopFacade { public static void buyHorse() { new HorseShop().sell(); } public static void buySaddle() { new SaddleShop().sell(); } public static void buyBridle() { new BridleShop().sell(); } public static void buyWhip() { new WhipShop().sell(); } public static void sellFood() {//供销社扩展的功能 System.out.println("买口粮"); } }
外观角色可以封装各个子系统的对象功能,并且也可以扩展一定的功能。此时客户角色只需要执行以下代码就可以取代原来的功能:
Client
public class Client { public static void main(String[] args) { func1(); func2(); } private static void func1() { ShopFacade.buyHorse(); ShopFacade.buySaddle(); ShopFacade.buyBridle(); ShopFacade.buyWhip(); } private static void func2() { ShopFacade.buySaddle(); ShopFacade.buyHorse(); ShopFacade.buyWhip(); ShopFacade.sellFood(); } }
如此一来,就实现了客户端与各个子系统之间的解耦,客户角色需要处理的对象数目就大大降低。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 【.NET】调用本地 Deepseek 模型