在生活中,我们如果需要自己动手做菜,那么需要经历买菜-->洗菜-->切菜-->烹饪等步骤,那么如果这些步骤都是独立的类,而客户端指的是做菜的人,那么客户端需要调用每一个类里相应的函数,那么这样会造成客户端里调用的类过多,一旦需要改变步骤,那么需要改动客户端代码,这是十分不友好的设计。我们可以提供一个类来负责这些步骤的调用,而客户端只需调用这个新加类的相应方法即可,而这个类就是相当于一个门面,能够将客户端从一个复杂的子系统中解耦出来。这就是我们今天讲的设计模式叫做门面模式或者外观模式。外观模式提供了一个统一的接口,用来访问子系统中的一群接口,外观提供了一个高层接口,让子系统更容易使用。
1.适用性和优缺点
1.适用性:
a.当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户的用户带来一些使用上的困难。Facade可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过facade层。
b.客户程序与抽象类的实现部分之间存在很大的依赖性。引入facade将这个子系统与客户以及其它的子系统分离,可以提高子系统的独立性和可移植性。
c.当你需要构建一个层次结构的子系统时,使用facade模式定义了子系统每层的入口点。如果子系统之间是相互依赖的,可以让它们仅通过facade进行通讯,从而简化了它们之间的依赖关系。
2.优点:
a.对客户屏蔽子系统部件,减少了客户处理的对象数目,并使子系统更易使用。
b.实现了子系统与客户之间的松耦合关系,子系统组件变化不影响客户端,只需要调整外观类即可。
c.降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程。
d.只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类。
3.缺点:
a.不能很好地限制客户端使用子系统类,若对客户访问子系统类作出太多的限制,则减少了可变性和灵活性。
b.在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或者客户端的源代码,违背了"开闭原则"。
2.示例讲解
看如下类图:
上面类图中子系统中有许多接口,如果让客户端直接调用这些接口的话,使得客户端与子系统依赖程度过高。因此引入了一个Facade类,负责具体使用这些接口,客户端只需要调用facade即可,这样可以将客户端从一个复杂的子系统中解耦。
接下来,我们来看示例的具体实现,首先定义各个子系统中的接口以及其实现类。
ServiceA接口及其实现类:
package facadepattern;
public interface ServiceA {
public void methodA();
}
package facadepattern;
public class ServiceAImpl implements ServiceA {
@Override
public void methodA() {
// TODO Auto-generated method stub
System.out.println("这是服务A");
}
}
ServiceB接口及其实现类:
package facadepattern;
public interface ServiceB {
public void methodB();
}
package facadepattern;
public class ServiceBImpl implements ServiceB {
@Override
public void methodB() {
// TODO Auto-generated method stub
System.out.println("这是服务B");
}
}
ServiceC及其接口:
package facadepattern;
public interface ServiceC {
public void methodC();
}
package facadepattern;
public class ServiceCImpl implements ServiceC {
@Override
public void methodC() {
// TODO Auto-generated method stub
System.out.println("这是服务C");
}
}
然后定义一个外观类Facade,外观类中定义了各个接口对象,并根据客户要求完成调用:
package facadepattern;
public class Facade {
//定义接口类型,便于统一操作
ServiceA sa;
ServiceB sb;
ServiceC sc;
//初始化子系统中的类
public Facade()
{
sa=new ServiceAImpl();
sb=new ServiceBImpl();
sc=new ServiceCImpl();
}
public void methodA()
{
sa.methodA();
sb.methodB();
}
public void methodB()
{
sb.methodB();
sc.methodC();
}
public void methodC()
{
sc.methodC();
sa.methodA();
}
}
然后客户端只需要调用外观类即可,客户端通过直接调用子类和调用外观类进行对比,如果客户端需要改变对子系统的接口的调用,前者需要在客户端改变,而后者只需要在外观类中修改即可:
package facadepattern;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
//不使用外观类
ServiceA sa=new ServiceAImpl();
ServiceB sb=new ServiceBImpl();
sa.methodA();
sb.methodB();
System.out.println("=======");
//使用外观类
Facade facade=new Facade();
facade.methodA();
facade.methodB();
}
}
运行以上程序,得到如下结果:
这是服务A
这是服务B
=======
这是服务A
这是服务B
这是服务B
这是服务C
以下是是否使用外观模式的一个对比图,从图中一眼就可以看出使用外观模式的优点。
在常见的设计框架中,也有用到外观模式的,比如:1.Spring框架中的Hibernate Template类就是一个外观类,它是SessionFactory、Session、Query等类的Facade,当客户端需要进行持久化查询时,无须调用这些具体类,只需要直接调用外观类即可。2.Jave EE应用中的Service层,就是一个Facade层,这层的类叫作业务逻辑组件,它们就是通过组合DAO组件的原子性操作来实现客户所需要的实际操作,以封装DAO组件的形式,使得系统的控制器不需要直接访问DAO组件,只需要访问外观类即业务逻辑组件即可。