Facade(外观)模式
外观模式,或者叫门面模式,是一种十分常用的结构性模式。具体体现在我们平常使用的第三方 SDK 上,在使用的过程中,可以发现这些 SDK 具有良好的封装性,我们在调用时是需要调用相关的方法就可以实现我们想要的功能,但是这个方法内部的具体实现我们一概不知,降低了我们的使用成本。简单的一句话总结就是:外观模式提供了一个高层次的接口,使得子系统更易于使用。
举一个生活中的例子,在我看来,去餐厅点餐就用到了外观模式。菜单就像是一个高层次的接口,将各种菜系和菜品都罗列在一个菜单上,用户根据自己的情况来点菜,但是用户不需要知道这个菜是怎么做吃来的,他只需要在点完菜后等待就可以了,而具体的如何做出用户点的菜由厨师来实现,这里厨师就相当于是一个一个的子系统。因此,用户通过菜单来决定吃什么,比直接去问厨师要简单的多。
Facade(外观)模式的 UML 类图
- Client:客户
- Facade:高层次的接口
- SubSystem:各个子系统,由 Facade 同一管理
外观模式的简单实现
根据前面提到的菜单的例子,那么就以菜单为例,实现一个简单的外观模式。首先是分析一下那个是高层接口,哪些是子系统?
- 菜单通过统一管理菜系和菜品名,来向用户展示这家餐厅有什么菜,所以菜单类应该为高层接口
- 各系菜系都有不同的实现,而且都属于同一家餐厅,所以各系菜系为子系统(或者说做菜的厨师也可以)
经过简单的分析过后,下面来实现一下这个案例
首先应该是实现各个子系统接口,这里假设这家餐厅有中,法,意三个菜系(那这家餐厅到底是中餐厅还是西餐厅???)
中国菜接口:ChineseCuisine
1 2 3 4 5 6 7 8 9 10 11 12
| public interface ChineseCuisine {
public void boiledChickenwithSauce();
public void sizzlingBeefSteak();
public void kungPaoChicken();
}
|
法国菜接口:FrenchCuisine
1 2 3 4 5 6 7 8 9 10 11 12
| public interface FrenchCuisine {
public void bouillabaisse();
public void cassoulet();
public void pouleAuPot();
}
|
意大利接口:ItalyCuisine
1 2 3 4 5 6 7 8 9 10 11 12
| public interface ItalyCuisine {
public void lasagneWithTomatoAndCheese();
public void prawnRisotto();
public void creamCaramel();
}
|
接口内部只是简单的罗列了一下,接下来就是具体的实现了
中国菜实现类:ChineseCuisineImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class ChineseCuisineImpl implements ChineseCuisine { @Override public void boiledChickenWithSauce() { System.out.println("白切鸡"); }
@Override public void sizzlingBeefSteak() { System.out.println("铁板牛肉"); }
@Override public void kungPaoChicken() { System.out.println("宫保鸡丁"); } }
|
法国菜实现类:FrenchCuisineImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class FrenchCuisineImpl implements FrenchCuisine { @Override public void bouillabaisse() { System.out.println("马赛鱼汤"); }
@Override public void cassoulet() { System.out.println("豆焖肉"); }
@Override public void pouleAuPot() { System.out.println("法式炖鸡"); } }
|
意大利菜实现类:ItalyCuisineImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class ItalyCuisineImpl implements ItalyCuisine { @Override public void lasagneWithTomatoAndCheese() { System.out.println("焗茄汁千层面"); }
@Override public void prawnRisotto() { System.out.println("虾仁烩饭"); }
@Override public void creamCaramel() { System.out.println("焦糖布丁"); } }
|
注意 关于具体实现的细节,这里是用注释加以代替,但在实际开发中,应该根据具体情况而定
以上,菜就算“做”好了,下面该实现菜单类了
菜单类:Menu
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| public class Menu {
private ChineseCuisine chineseCuisine; private FrenchCuisine frenchCuisine; private ItalyCuisine italyCuisine;
public Menu() { chineseCuisine = new ChineseCuisineImpl(); frenchCuisine = new FrenchCuisineImpl(); italyCuisine = new ItalyCuisineImpl(); }
public void boiledChickenWithSauce() { chineseCuisine.boiledChickenWithSauce(); }
public void sizzlingBeefSteak() { chineseCuisine.sizzlingBeefSteak(); }
public void kungPaoChicken() { chineseCuisine.kungPaoChicken(); }
public void bouillabaisse() { frenchCuisine.bouillabaisse(); }
public void cassoulet() { frenchCuisine.cassoulet(); }
public void pouleAuPot() { frenchCuisine.cassoulet(); }
public void lasagneWithTomatoAndCheese() { italyCuisine.lasagneWithTomatoAndCheese(); }
public void prawnRisotto() { italyCuisine.prawnRisotto(); }
public void creamCaramel() { italyCuisine.creamCaramel(); }
}
|
经过以上的步骤过后,就可以简单的测试一下了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Menu menu = new Menu();
System.out.println("客户一点餐:"); menu.boiledChickenWithSauce(); menu.lasagneWithTomatoAndCheese(); menu.sizzlingBeefSteak();
System.out.println("\n+-----------+\n");
System.out.println("客户二点餐:"); menu.cassoulet(); menu.pouleAuPot(); menu.creamCaramel();
|
测试结果如下
1 2 3 4 5 6 7 8 9 10 11
| 客户一点餐: 白切鸡 焗茄汁千层面 铁板牛肉
+-----------+
客户二点餐: 豆焖肉 法式炖鸡 焦糖布丁
|
可见,Menu 的作用就是同一管理各个菜,通过一份菜单客户就可以点到各种各样的菜,降低了用户的使用(点餐)成本,使用起来更加简单
总结
通过将各个子系统封装起来,并通过一个高层次的结构向用户提供一个统一的 API 入口,使得这些子系统的使用更加的灵活并且降低了用户的使用成本。所以这里总结一下外观模式的优缺点:
END.
源码地址