外观模式本身理解起来是很容易的,也就一句话 talk is cheap , show you code
情景假设
你家养了一个机器人,笨笨,它很笨,而你经常让他给你冲咖啡,但是步骤都要你一个一个去指示,比如,放咖啡粉,倒热水,搅拌搅拌,都要去下命令让他完成,而如果想摆脱这些繁杂的步骤,使用外观模式就行了,这样你的指令就是,去帮我泡杯咖啡。(下面例子中注意对比加不加外观模式源程序和类图的区别)。
java代码示例
加咖啡粉
package facade;
public class AddCoffeeFlour {
public void addCoffeeFlour(){
System.out.println("+coffee flour");
}
}
加热水
package facade;
public class PourHotWater {
public void pourHotWater(){
System.out.println("pour hot water");
}
}
搅拌
package facade;
public class StirCoffee {
public void stirCoffee(){
System.out.println("搅拌.....");
}
}
客户端
package facade;
public class Client {
public static void main(String[] args) {
AddCoffeeFlour acf=new AddCoffeeFlour();
PourHotWater phw=new PourHotWater();
StirCoffee sc=new StirCoffee();
acf.addCoffeeFlour();//加咖啡粉
phw.pourHotWater();//冲热水
sc.stirCoffee();//搅拌
}
}
运行结果
此时的类图为
现在我们引入外观模式
添加新类 泡咖啡
package facade;
public class MakeCoffee {
AddCoffeeFlour acf;
PourHotWater phw;
StirCoffee sc;
//对子系统对象包装
public MakeCoffee() {
acf=new AddCoffeeFlour();
phw=new PourHotWater();
sc=new StirCoffee();
}
public void makeCoffee() {//调用泡咖啡的方法即可
acf.addCoffeeFlour();
phw.pourHotWater();
sc.stirCoffee();
}
}
客户端改为
package facade;
public class Client {
public static void main(String[] args) {
MakeCoffee mc=new MakeCoffee();
mc.makeCoffee();
}
}
运行结果不变,但此时类图为
对比分析
在实现同样功能的情况下外观模式比起源程序又引入了一个新的类,这个类的作用就是替你管理复杂的子系统,这样客户端就不用直接和复杂的系统打招呼,直接和这个引入的管理类打招呼,一样能完成需要的工作,但是很明显客户端做的工作变少,调用变的更简洁,下面我们再重新理解外观模式。
定义
外观模式提供了一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更容易使用。
应用场景
上例表现的很明显了,当你所使用的系统较为复杂的时候,不妨花点时间,建立外观,把接口统一起来,对外提供合适的调用方法,这样在使用系统的时候直接对外观操作就变的简单。
用到的设计原则
迪米特法则-------高内聚,低耦合(也叫最少知识原则)
原则就是,尽量减少对象之间的交互,降低耦合,在本例中,外观模式实现了客户端与子系统之间的解耦。
优点
-
- 简化了接口
- 实现了客户端与子系统的解耦
- 外观模式并没有隔绝子系统,如果想直接调用底层,还是可以的
缺点
1. 只要使用外观模式就免不了建立包装类,总是会增加类的复杂度(不过相比降低调用的复杂度,这是小问题)
2.包装类里面集合了所有类的实例的创建逻辑,直接违反了高内聚,低耦合的原则
3.如果步骤发生改变,比如咖啡需要加糖,就需要修改包装类的代码,违反了开闭原则。
外观模式和装饰者适配器的对比
这三种模式都是结构型的,其实可以很明显的可以看出三种模式都是通过建立包装类来实现的,虽然做法相近,但是他们包装的目的是有很大差别的
外观模式 | 包装对象以简化复杂的接口 |
适配器 | 包装对象是为了把接口装换成想要的接口 |
装饰者 | 包装对象是为了增加新的行为 |