《三国演义》中有曰:刘备、诸葛亮趁曹操赤壁之战失利,大肆扩充地盘,先后占领荆州大部地区,引起东吴孙权的警惕。为了限制刘备势力的发展,鲁肃奉命向刘备讨还荆州,但遭到拒绝。东吴大都督周瑜向孙权献计:趁刘备的甘夫人病故,用孙权的妹妹孙仁为诱饵,将刘备“赚到南徐,妻子不能勾得,幽囚在狱中”。 但是,这个诡计被诸葛亮一眼识破。他将计就计,让刘备“择日便去就亲”,并派赵云前去保护,并给了赵云三个锦囊,教赵云“依次而行”。结果,使东吴“赔了夫人又折兵”。此为诸葛亮的锦囊三妙计。
一、三妙计
妙计一:见乔国老,并把刘备娶亲的事情搞得东吴人尽皆知。
妙计二:用谎言(曹操打荆州)骗泡在温柔乡里的刘备回去。
妙计三:让孙夫人摆平东吴的追兵,她是孙权妹妹,东吴将领惧她三分。
二、妙计的两种实施方案
1.把三个锦囊直接交给刘备,让刘备根据情况打开锦囊。
2.把锦囊交给赵云,赵云按照诸葛亮的嘱咐,依次按照情况使用锦囊。
三、刘备用妙计
①妙计的接口
public interface Strategy {
//妙计内容
public void carryOut();
}
复制代码
②妙计一
public class StrategyOne implements Strategy {
@Override
public void carryOut() {
System.out.println("见乔国老,并把刘备娶亲的事情搞得东吴人尽皆知。");
}
}
复制代码
③妙计二
public class StrategyTwo implements Strategy {
@Override
public void carryOut() {
System.out.println("用谎言(曹操打荆州)骗泡在温柔乡里的刘备回去。");
}
}
复制代码
④妙计三
public class StrategyThree implements Strategy {
@Override
public void carryOut() {
System.out.println("让孙夫人摆平东吴的追兵。");
}
}
复制代码
⑤刘备使用妙计
public class Client {
public static void main(String[] args) {
//刘备一行人到达南徐的时候,打开第一个锦囊
Strategy strategyOne=new StrategyOne();
strategyOne.carryOut();
//周瑜和孙权通过计谋使刘备沉迷在温柔乡无法自拔
Strategy strategyTwo=new StrategyTwo();
strategyTwo.carryOut();
//周瑜看计谋不行,出兵拦杀刘备
Strategy strategyThree=new StrategyThree();
strategyThree.carryOut();
}
}
复制代码
输出的结果为:
//刘备一行人到达南徐的时候,打开第一个锦囊
见乔国老,并把刘备娶亲的事情搞得东吴人尽皆知。
//周瑜和孙权通过计谋使刘备沉迷在温柔乡无法自拔
用谎言(曹操打荆州)骗泡在温柔乡里的刘备回去。
//周瑜看计谋不行,出兵拦杀刘备
让孙夫人摆平东吴的追兵。
复制代码
忽略其他正常因素(诸葛亮不可能把锦囊交给刘备实施),代码角度分析这种方案带来的问题
锦囊使用是有前提的,要根据每个阶段来使用对应的锦囊,在编写代码中要清楚它们的顺序,一旦出错就会造成刘备死翘翘。这在面向对象的编程中是极度地不适合,它根本就没有完成一个类所具有的单一职责。
外界访问直接深入到子系统内部,相互之间是一种强耦合关系,这样的强依赖是系统设计所不能接受的。
子系统的内部方法直接暴露给外部调用,安全性低。
当锦囊数越来越多的时候,那么Client就需要调用更多锦囊类来实现,这就会增加Client的实现难度,维护更加困难。
四、赵云协助用妙计(门面模式)
诸葛亮能够想到应对之策,当然也会考虑到让谁和如何实施这三个锦囊才能够让刘备顺利化险为夷。综合上面刘备亲自实施锦囊妙计带来的问题,安排赵云保管锦囊并在适当的时机协助实施才是上上之策。
1.UML实现
增加了一个ZhaoYunFacadee类,负责对锦囊实施过程进行封装,然后高层模块只要和它有交互就成。
2.增加的代码模块
①赵云充当门面
public class ZhaoYunFacade {
//妙计一
private Strategy strategyOne=new StrategyOne();
//妙计二
private Strategy strategyTwo=new StrategyTwo();
//妙计三
private Strategy strategyThree=new StrategyThree();
//三妙计统一让赵云协助实施
public void carryOut(){
//刘备一行人到达南徐的时候,打开第一个锦囊
strategyOne.carryOut();
//周瑜和孙权通过计谋使刘备沉迷在温柔乡无法自拔
strategyTwo.carryOut();
//周瑜看计谋不行,出兵拦杀刘备
strategyThree.carryOut();
}
}
复制代码
刘备在遇到困难的时候,只要让赵云根据诸葛亮的锦囊进行处理,这多简单,Client减少了很多工作。
②Client
public class Client {
public static void main(String[] args) {
//刘备遇到困难,只要通过赵云按照锦囊实施,就可化险为夷
ZhaoYunFacade zhaoYunFacade=new ZhaoYunFacade();
zhaoYunFacade.carryOut();;
}
}
复制代码
输出的结果为:
//刘备一行人到达南徐的时候,打开第一个锦囊
见乔国老,并把刘备娶亲的事情搞得东吴人尽皆知。
//周瑜和孙权通过计谋使刘备沉迷在温柔乡无法自拔
用谎言(曹操打荆州)骗泡在温柔乡里的刘备回去。
//周瑜看计谋不行,出兵拦杀刘备
让孙夫人摆平东吴的追兵。
复制代码
运行结果是相同的。场景类简化了很多,只要与ZhaoYunFacade交互就成了,其他的什么都不用管,什么时候使用锦囊、怎么用都不用管,只要调用ZhaoYunFacade提供的方法,就可以得到想要的妙计,这种方式不仅简单,而且扩展性还非常好,同时不改变子系统对外暴露的接口、方法,只改变内部的处理逻辑,其他兄弟模块的调用产生了不同的结果。
3.两种方案的程序结构图
- 没有采用门面模式
- 采用门面模式
总的来说,门面对象是外界访问子系统内部的唯一通道,不管子系统内部是多么杂乱无章。
五、门面模式的介绍
1.门面模式的定义
门面模式(Facade Pattern)也叫做外观模式。要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。
2.中介模式的角色介绍
Facade门面角色
客户端可以调用这个角色的方法。此角色知晓子系统的所有功能和责任。一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去,也就说该角色没有实际的业务逻辑,只是一个委托类。subsystem子系统角色
可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另外一个客户端而已。
3.门面模式的使用场景
为一个复杂的模块或子系统提供一个供外界访问的接口。
为降低个人代码质量对整体项目的影响风险,只能在指定的子系统中开发,然后再提供门面接口进行访问操作。
子系统相对独立——外界对子系统的访问只要黑箱操作即可。