原博文:https://blog.csdn.net/KongZhongNiao/article/details/79568736
定义
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层
接口,这个接口使得这一子系统更加容易使用。
结构和说明
- Facade:定义子系统的多个模块对外的高层接口,通常需要调用内部多个模块,
从而把客户的请求代理给适当的子系统对象。
- 模块:接受Facade对象的委派,真正实现功能,各个模块之间可能有交互。
- 注意,Facade对象知道各个模块,但是各个模块不应该知道Facade对象。
示例代码
A模块的接口
package cn.javass.dp.facade.example2;
/**
* A模块的接口
*/
public interface AModuleApi {
/**
* 示意方法,A模块对外的一个功能方法
*/
public void testA();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
A模块的接口的实现
package cn.javass.dp.facade.example2;
public class AModuleImpl implements AModuleApi{
public void testA() {
System.out.println("现在在A模块里面操作testA方法");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
B模块的接口
package cn.javass.dp.facade.example2;
public interface BModuleApi {
public void testB();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
B模块的接口的实现
package cn.javass.dp.facade.example2;
public class BModuleImpl implements BModuleApi{
public void testB() {
System.out.println("现在在B模块里面操作testB方法");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
C模块的接口
package cn.javass.dp.facade.example2;
public interface CModuleApi {
public void testC();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
C模块的接口的实现
package cn.javass.dp.facade.example2;
public class CModuleImpl implements CModuleApi{
public void testC() {
System.out.println("现在在C模块里面操作testC方法");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
外观对象
package cn.javass.dp.facade.example2;
/**
* 外观对象
*/
public class Facade {
/**
* 示意方法,满足客户需要的功能
*/
public void test(){
//在内部实现的时候,可能会调用到内部的多个模块
AModuleApi a = new AModuleImpl();
a.testA();
BModuleApi b = new BModuleImpl();
b.testB();
CModuleApi c = new CModuleImpl();
c.testC();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
客户端
package cn.javass.dp.facade.example2;
public class Client {
public static void main(String[] args) {
// //不用Facade,需要自己跟多个模块交互
// AModuleApi a = new AModuleImpl();
// a.testA();
// BModuleApi b = new BModuleImpl();
// b.testB();
// CModuleApi c = new CModuleImpl();
// c.testC();
//
// System.out.println("使用Facade----------------------〉");
//使用了Facade
new Facade().test();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
生活中的示例——组装电脑
1:完全自己组装
2:找专业装机公司组装
代码生成的应用
问题
如果现在客户端需要使用这个代码生成工具来生成需要的基础代码,该如
何实现呢?
不用模式的解决方案
直接参看代码示例
示意生成数据层的模块
package cn.javass.dp.facade.example1;
/**
* 示意生成数据层的模块
*/
public class DAO {
public void generate(){
//1:从配置管理里面获取相应的配置信息
ConfigModel cm = ConfigManager.getInstance().getConfigData();
if(cm.isNeedGenDAO()){
//2:按照要求去生成相应的代码,并保存成文件
System.out.println("正在生成数据层代码文件");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
示意生成表现层的模块
package cn.javass.dp.facade.example1;
/**
* 示意生成表现层的模块
*/
public class Presentation {
public void generate(){
//1:从配置管理里面获取相应的配置信息
ConfigModel cm = ConfigManager.getInstance().getConfigData();
if(cm.isNeedGenPresentation()){
//2:按照要求去生成相应的代码,并保存成文件
System.out.println("正在生成表现层代码文件");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
示意生成逻辑层的模块
package cn.javass.dp.facade.example1;
/**
* 示意生成逻辑层的模块
*/
public class Business {
public void generate(){
//1:从配置管理里面获取相应的配置信息
ConfigModel cm = ConfigManager.getInstance().getConfigData();
if(cm.isNeedGenBusiness()){
//2:按照要求去生成相应的代码,并保存成文件
System.out.println("正在生成逻辑层代码文件");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
示意配置描述的数据Model,真实的配置数据会很多
package cn.javass.dp.facade.example1;
/**
* 示意配置描述的数据Model,真实的配置数据会很多
*/
public class ConfigModel {
/**
* 是否需要生成表现层,默认是true
*/
private boolean needGenPresentation = true;
/**
* 是否需要生成逻辑层,默认是true
*/
private boolean needGenBusiness = true;
/**
* 是否需要生成DAO,默认是true
*/
private boolean needGenDAO = true;
public boolean isNeedGenPresentation() {
return needGenPresentation;
}
public void setNeedGenPresentation(boolean needGenPresentation) {
this.needGenPresentation = needGenPresentation;
}
public boolean isNeedGenBusiness() {
return needGenBusiness;
}
public void setNeedGenBusiness(boolean needGenBusiness) {
this.needGenBusiness = needGenBusiness;
}
public boolean isNeedGenDAO() {
return needGenDAO;
}
public void setNeedGenDAO(boolean needGenDAO) {
this.needGenDAO = needGenDAO;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
示意配置管理,就是负责读取配置文件,
package cn.javass.dp.facade.example1;
/**
* 示意配置管理,就是负责读取配置文件,
* 并把配置文件的内容设置到配置Model中去,是个单例
*/
public class ConfigManager {
private static ConfigManager manager = null;
private static ConfigModel cm = null;
private ConfigManager(){
}
public static ConfigManager getInstance(){
if(manager == null){
manager = new ConfigManager();
cm = new ConfigModel();
//读取配置文件,把值设置到ConfigModel中去
}
return manager;
}
/**
* 获取配置的数据
* @return 配置的数据
*/
public ConfigModel getConfigData(){
return cm;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
客户端
package cn.javass.dp.facade.example1;
public class Client {
public static void main(String[] args) {
//现在没有配置文件,就直接使用默认的配置
//通常情况下,三层都应该生成,也就是说客户端必须
//对这些模块都有了解,才能够正确使用它们
new Presentation().generate();
new Business().generate();
new DAO().generate();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
存在的问题
客户端为了使用生成代码的功能,需要与生成代码子系统内部的多个模块交互。
使用模式的解决方案
红线内表示被封装隔离开
红线外facade表示与客户端交互
使用外观模式的示例代码
其他类没有变,增添了facade外观类
代码生成子系统的外观对象
package cn.javass.dp.facade.example3;
/**
* 代码生成子系统的外观对象
*/
public class Facade {
/**
* 客户端需要的,一个简单的调用代码生成的功能
*/
public void generate(){
new Presentation().generate();
new Business().generate();
new DAO(2).generate();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
客户端
package cn.javass.dp.facade.example3;
public class Client {
public static void main(String[] args) {
new Facade().generate();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
理解外观模式
认识外观模式
1:外观模式的目的
外观模式的目的不是给子系统添加新的功能接口,而是为了让外部减少与
子系统内多个模块的交互,松散耦合,从而让外部能够更简单的使用子系统。2:使用外观跟不使用相比有何变化
Facade方便了客户端的调用、封装了系统内部的细节功能、实现功能的共
享和复用3:有外观,但是可以不使用
4:外观提供了缺省的功能实现
- 5: 外观模式的调用顺序示意图
外观模式的实现
- 1:把外观类当成一个辅助工具类实现
代码生成子系统的外观对象
package cn.javass.dp.facade.example3;
/**
* 代码生成子系统的外观对象
*/
public class Facade {
private Facade(){
}
/**
* 客户端需要的,一个简单的调用代码生成的功能
*/
public static void generate(){
new Presentation().generate();
new Business().generate();
new DAO(2).generate();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
客户端
package cn.javass.dp.facade.example3;
public class Client {
public static void main(String[] args) {
Facade.generate();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 2:Facade可以实现成为interface
Facadeapi接口
package cn.javass.dp.facade.example5;
public interface FacadeApi {
public void generate();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
Facade实现
package cn.javass.dp.facade.example5;
/**
* 代码生成子系统的外观对象
*/
public class Facade implements FacadeApi{
/**
* 客户端需要的,一个简单的调用代码生成的功能
*/
public void generate(){
new Presentation().generate();
new Business().generate();
new DAO(2).generate();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
Facade工厂
package cn.javass.dp.facade.example5;
public class FacadeFactory {
private FacadeFactory(){
}
public static FacadeApi createFacadeApi(){
return new Facade();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
客户端
package cn.javass.dp.facade.example5;
public class Client {
public static void main(String[] args) {
FacadeApi api = FacadeFactory.createFacadeApi();
api.generate();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 3:Facade实现成为interface的附带好处
能够有选择性的暴露接口方法,尽量减少模块对子系统外提供的接口方法。
AModuleApi
package cn.javass.dp.facade.example4;
public interface AModuleApi {
//提供给子系统外部使用
public void a1();
//提供给同一个子系统中,不同的模块使用,比如给B模块,给C模块
public void a2();
public void a3();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
BModuleApi
package cn.javass.dp.facade.example4;
public interface BModuleApi {
//对子系统外部
public void b1();
//子系统内部使用
public void b2();
//子系统内部使用
public void b3();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
CModuleApi
package cn.javass.dp.facade.example4;
public interface CModuleApi {
//对子系统外部
public void c1();
//子系统内部使用
public void c2();
//子系统内部使用
public void c3();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
FacadeApi
package cn.javass.dp.facade.example4;
public interface FacadeApi {
public void a1();
public void b1();
public void c1();
public void test();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 4:Facade的方法实现
Facade的方法实现中,一般是负责把客户端的请求转发给子系统内部的各
个模块进行处理,Facade的方法本身并不进行功能的处理,Facade的方法的实现
只是实现一个功能的组合调用。
思考外观模式
外观模式的优缺点
- 1:松散耦合
- 2:简单易用
- 3:更好的划分访问层次
- 4:过多的或者是不太合理的Facade也容易让人迷惑
外观模式的本质
外观模式的本质是:封装交互,简化调用
对设计原则的体现
体现了“最少知识原则”
何时选用外观模式
- 1:如果你希望为一个复杂的子系统提供一个简单接口的时候,可以考虑使用外观模
式,使用外观对象来实现大部分客户需要的功能,从而简化客户的使用 - 2:如果想要让客户程序和抽象类的实现部分松散耦合,可以考虑使用外观模式,使
用外观对象来将这个子系统与它的客户分离开来,从而提高子系统的独立性和可
移植性 - 3:如果构建多层结构的系统,可以考虑使用外观模式,使用外观对象作为每层的入
口,这样可以简化层间调用,也可以松散层次之间的依赖关系