外观模式是软件工程中常用的一种软件设计模式。它为子系统中的一组接口提供一个统一的高层接口。这一接口使得子系统更加容易使用。

它的表现很简单,将一系列子接口的功能进行整理,从而产生一个更高层的接口。

外观模式的标准类图:

JAVA平面图 java设计图_JAVA平面图

外观模式的类图,它主要由两部分组成,一部分是子系统(包括接口,实现类,等等),一部分是外观接口和实现类,外观接口负责提供客户端定制的服务,外观实现则负责组合子系统中的各个类和接口完成这些服务,外观接口则是提供给客户端使用的,这样就解除了客户端与子系统的依赖,而让客户端只依赖于外观接口,这是一个优秀的解耦实践。

使用JAVA代码来直观的看看外观模式的实现方式。首先是我们的子系统,它包括三个接口,三个实现这里一并给出。

package com.facade;
public interface Sub1 {
void function1();
}
package com.facade;
public interface Sub2 {
void function2();
}
package com.facade;
public interface Sub3 {
void function3();
}
package com.facade;
public class Sub1Impl implements Sub1{
public void function1() {
System.out.println("子系统中Sub1接口的功能");
}
}
package com.facade;
public class Sub3Impl implements Sub3{
public void function3() {
System.out.println("子系统中Sub3接口的功能");
}
}

接口Facade以及它的简单实现:

package com.facade;
public interface Facade {
/* 下面随便组装几个功能 */
void function12();
void function23();
void function123();
}
package com.facade;
public class FacadeImpl implements Facade{
private Sub1 sub1;
private Sub2 sub2;
private Sub3 sub3;
public FacadeImpl() {
super();
this.sub1 = new Sub1Impl();
this.sub2 = new Sub2Impl();
this.sub3 = new Sub3Impl();
}
public FacadeImpl(Sub1 sub1, Sub2 sub2, Sub3 sub3) {
super();
this.sub1 = sub1;
this.sub2 = sub2;
this.sub3 = sub3;
}
public void function12() {
sub1.function1();
sub2.function2();
}
public void function23() {
sub2.function2();
sub3.function3();
}
public void function123() {
sub1.function1();
sub2.function2();
sub3.function3();
}
}

客户端的调用:

package com.facade;
public class Client {
public static void main(String[] args) {
Facade facade = new FacadeImpl();
facade.function12();
System.out.println("-------------------------");
facade.function23();
System.out.println("-------------------------");
facade.function123();
/* 以上为使用了外观模式的调用方式,以下为原始方式 */
System.out.println("-------------以下原始方式--------------");
Sub1 sub1 = new Sub1Impl();
Sub2 sub2 = new Sub2Impl();
Sub3 sub3 = new Sub3Impl();
sub1.function1();
sub2.function2();
System.out.println("-------------------------");
sub2.function2();
sub3.function3();
System.out.println("-------------------------");
sub1.function1();
sub2.function2();
sub3.function3();
}
}

可以看出在外观模式的作用下,我们客户端只依赖外观一个接口,而在原始的方式下,我们的客户端依赖于整个子系统,所以外观模式主要解决的是类之间的耦合过于复杂。

总结:

1,实际使用当中,接口并不是必须的,虽说根据依赖倒置原则,无论是处于高层的外观层,还是处于底层的子系统,都应该依赖于抽象,但是这会导致子系统的每一个实现都要对应一个接口,从而导致系统的复杂性增加,所以这样做并不是必须的。

2,外观接口当中并不一定是子系统中某几个功能的组合,也可以是将子系统中某一个接口的某一功能单独暴露给客户端。

3,外观接口如果需要暴露给客户端很多的功能的话,可以将外观接口拆分为若干个外观接口,如此便会形成一层外观层。

第三点,便是为了引出service层,web开发项目中有很多的service和dao(注:小型项目或许不需要service这一层),这一层service层,有一个非常重要的作用,就是为了方便我们管理项目中与业务逻辑相关的事物,而service层,其实是给我们的事务管理器提供了一个可以方便的配置切入点的事务管理层。

service层同时也是组合dao层暴露给action的功能,dao层的各个类只是简单的数据操作对象,它们不具有业务逻辑,而赋予了它们业务逻辑方便action调用的正是service这一层。假设没有service这一层,action当中有很多功能需要依赖更多的dao才可以完成工作。

同时在WEB项目中,有的项目会抽象出一层service接口和一层dao接口,这是为了降低客户端(这里的客户端可以认为是action)与业务实现细节以及service外观层与数据操作实现细节的耦合;

而有的项目则没有抽象层,这也并非就是不合适的。首先添加抽象层会大大的加剧项目的类文件数量,从而使项目的复杂性增加,而且在项目刚进入开发的时候,往往接口是不稳定的,因为我们经常会需要要给某一个service添加一个方法,而为了将方法暴露给客户端(即action),我们必须将该方法添加到对应的接口当中。

所以针对这一情况,我们更好的做法是等到接口行为相对稳定时,再考虑是否要重构去添加抽象的接口,而且现在的IDE工具都在一定程度上对重构进行了支持,比如eclipse就可以直接导出一个类的接口,所以完全可以在需要时快速的给项目添加抽象的接口层。

相比起观察者模式、适配器模式等适合小规模使用的设计模式,外观模式更多的是大范围的使用,它会是很多时候支撑我们整个架构的设计思路。

外观模式并不是简单的使用组合将功能组合起来,也就是说它的重点不在组合功能,而在于制作一个对外暴露的外观。它一般是用来将一个子系统(注意,是一个子系统,也就是说外观并不是简单的几个类的组合就是外观模式了)的功能进行调配,暴露给客户端一个外部的表象,使得客户端与子系统断开依赖关系。