生活实例
图例中的"装机公司"就是一种"外观模式",我们不需要与众多的硬打交道,只需要与装机公司联系,就可以组装电脑了
外观模式定义
为系统中的一组接口提供一个一直的界面,Facade模式定义了一个高层接口,这个接口使得子系统更加容易使用.
- 组件界面--从一个组件的外部来观看这个组件,能看到的界面,比如:一个类的public方法就是这个类的界面,一个模块外部看这个模块,模块的接口就是这个模块的界面.
- 接口--模块的外部与模块之间交互的一个通道,通常是类的方法或接口方
外观模式结构图
- Facade--定义子系统的多个模块对外的高层接口,通常需要调用内部多个模块,从而把客户的请求代理给适当的子系统对象
- 模块--接受Facade对象的委派,真正实现功能,各个模块之间可能有交互,但注意,Facade对象知道各个模块,但是各个模块不应该知道Facade对象.
实例代码
/*
A模块接口
*/
public interface AModuleApi {
public void testA();
}
/*
A模块实现类
*/
public class AModuleImpl implements AModuleApi {
public void testA() {
sysout("正在A模块,操作A方法");
}
}
/*
B模块接口
*/
public interface BModuleApi {
public void testB();
}
/*
B模块实现类
*/
public class BModuleImpl implements BModuleApi {
public void testB() {
sysout("正在B模块,操作B方法");
}
}
/*
C模块接口
*/
public interface CModuleApi {
public void testC();
}
/*
C模块实现类
*/
public class CModuleImpl implements CModuleApi {
public void testC() {
sysout("正在C模块,操作C方法");
}
}
/*
定义外观类
*/
public class Facade {
public void test() {
AModuleApi a = new AmoduleImpl();
a.testA();
BModuleApi b = new BmoduleImpl();
b.testB();
CModuleApi a = new CmoduleImpl();
c.testC();
}
}
/*
客户端代码
*/
public class client {
public static void main(String[] args) {
new Facade().test();
}
}
认识外观模式
外观模式的目的不是给子系统添加新的功能接口,而是为了让外部减少与子系统内部多个模块的交互,松散耦合,从而让外部能够更简单的使用子系统,这点需要特别注意,因为外观模式是当作子系统对外的接口出现的,虽然可以在这里定义一些子系统没有的功能,但不建议这么做.外观应该是包装已有的功能,主要是负责组合已有功能实现客户需要,而不是添加新的实现.
从表面上来看,只是将客户端的代码放到了Facade里面,但实际上还是有区别的,Facade相当于屏蔽了外部客户端和系统内部的模块交互,从而把A,B,C模块组合成为一个整体对外,不但方便了客户端的调用,而且封装了系统内部细节,这样如果有调用模块的算法发生变化,只需要修改Facade的界面即可,另外一个好处是,多个客户端调用的话,Facade实现了功能服用,同样的代码已经在Facade里面写好了,还有一个潜在的好处就是使用Facade的人员大大节省了学习成本,只需要了解Facade即可,无需深入学习子系统内部,去了解每个模块的细节,也不用同多个模块交互,学习也更容易.
虽然有了外观,但是外部仍然是可以绕开Facade界面,而直接调用某个具体模块的接口,这样既能兼顾组合功能与细节功能,例如,客户端需要使用A模块功能,可以直接调用A模块的接口,不需调用Facade界面.
外观模式调用示意图
外观模式的实现
对于一个子系统而言,外观类不需要很多,通常可以设计为单例
public class Facade {
private Facade(){
}
public static void test() {
AModuleApi a = new AmoduleImpl();
a.testA();
BModuleApi b = new BmoduleImpl();
b.testB();
CModuleApi a = new CmoduleImpl();
c.testC();
}
}
外观模式的本质
封装交互,简单调用
何时选用外观模式
- 如果希望为一个复杂的子系统提供一个简单接口的时候,可以考虑使用外观模式,使用外观对象来实现大部分客户端需要的功能,从而简化客户端的使用.
- 如果想要让客户端程序和抽象的实现部分松散耦合,可以考虑使用外观模式,使用外观对象将这个子系统与它的客户分离开,从而提高子系统的独立性和可移植性.
- 如果构建多层的系统,可以考虑使用外观模式,使用外观对象作为每层的入口,这样可以简化层见调用,也可以松散层次之间的依赖关系.
示例代码
public interface AMoudleInterface {
public void testA();
}
------------------------------------------------------------------
public class AMoudleImpl implements AMoudleInterface {
@Override
public void testA() {
System.err.println("正在操作A接口的A方法");
}
}
------------------------------------------------------------------
public interface BMoudleInterface {
public void testB();
}
------------------------------------------------------------------
public class BMoudleImpl implements BMoudleInterface {
@Override
public void testB() {
System.err.println("正在操作B接口的B方法");
}
}
------------------------------------------------------------------
//外观类,对多次操作进行封装
public class FacadeModule {
public void test() {
AMoudleImpl aMoudleImpl = new AMoudleImpl();
aMoudleImpl.testA();
BMoudleImpl bMoudleImpl = new BMoudleImpl();
bMoudleImpl.testB();
}
}
------------------------------------------------------------------
public class Client {
public static void main(String[] args) {
//外观模式封装了对一组接口的操作细节
//原始调用方法
AMoudleImpl aMoudleImpl = new AMoudleImpl();
aMoudleImpl.testA();
BMoudleImpl bMoudleImpl = new BMoudleImpl();
bMoudleImpl.testB();
System.err.println("====================================");
//外观模式后
//可以使用外观模式组合调用,也可以根据需要单独调用
FacadeModule facadeModule = new FacadeModule();
facadeModule.test();
}
}