外观模式
定义:为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口是的这一子系统更加容易使用。
结构和说明:
Facade:定义子系统的多个模块对外的高层接口,通常需要调用内部多个模块,从而吧客户的请求代理给适当的子系统对象。
模块:接受Facade对象的委派,真正实现功能,各个模块之间可能有交互。注意,Facade对象知道各个模块,但是各个模块不应该知道Facade对象。
举例:
//示意生成表现层的模块
public class Presentation {
public void generate() {
ConfigModel cm = ConfigManager.getInstance().getConfigData();
if (cm.isNeedGenPresentation()) {
System.out.println("正在生成表现层代码文件");
}
}
}
//示意生成逻辑层的模块
public class Business {
public void generate() {
ConfigModel cm = ConfigManager.getInstance().getConfigData();
if (cm.isNeedBusiness()) {
System.out.println("正在生成逻辑层代码文件");
}
}
}
//示意生成数据层的模块
public class DAO {
public void generate() {
ConfigModel cm = ConfigManager.getInstance().getConfigData();
if (cm.isNeedDAO()) {
System.out.println("正在生成数据层代码文件");
}
}
}
//配置管理
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;
}
//获取配置的数据
public ConfigModel getConfigData() {
return cm;
}
}
//示意配置描述的数据Model,真实的配置数据会很多
public class ConfigModel {
//是否需要生成表现层,默认true
private boolean needGenPresentation = true;
//是否需要生成逻辑层,默认true
private boolean needGenBusiness = true;
//是否需要生成DAO,默认true
private boolean needDAO = true;
//开放getset方法
}
不使用外观模式,客户端这样写
public class Client {
public static void main(String[] args) {
//现在没有配置文件,就直接使用默认的配置
//通常情况下,三层都应该生成,也就是说客户端必须
//对这些模块都有了解,才能够正确使用它们
new Presentation().generate();
new Business().generate();
new DAO().generate();
}
}
如果说模块变了,客户端的调用可能也要变化,这是非常糟糕的
存在的问题:客户端为了使用生成代码的功能,需要与生成代码子系统内部的多个模块交互。
使用模式的解决方案:
//代码生成子系统的外观对象
public class Facade {
//客户端需要的,一个简单的调用代码生成的功能
public void generate() {
new Presentation().generate();
new Business().generate();
new DAO().generate();
}
}
//客户端
public class Client {
public static void main(String[] args) {
new Facade().generate();
}
}
这样客户端就不需要与模块的内部打交道了,只与Facade打交道
外观模式的目的:
外观模式的目的不是给子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单的使用子系统。
Facade是为了让客户端和子系统分别开,实际上是帮客户端转调了这些内部的实现。包装、组合已有的功能,来满足客户端的功能。
使用外观和不使用外观相比有何变化:
Facade方便了客户端的调用、封装了系统内部的细节功能、实现功能的共享和复用。
Facade可以看成是对客户端的接口,当然这个接口不是interface,而是客户端与子系统交互的通道,好比你做出租车,司机相当于Facade,出租车的功能是子系统,我们是客户端,我们不需要自己去驾驶出租车,而是让司机去操作,而我们只说一句到哪,司机就操作一系列的汽车功能比如导航等把我们送到目的地。如果我们自己去驾驶的话就会有压力,我们要做的是要到达目的地,但是为了到达目的地还要学习驾驶汽车,显然很麻烦,在代码中也是如此,客户端要关心的事情太多,要知道各个模块的存在和功能,如果要传参的话它还要了解内部的细节,否则没有办法正确的使用。共享和复用也不难理解,如果有多个客户端,你不能每次都写那三行代码吧,这就好比一辆出租车只能拉同一个人,其余人不能拉,也是不现实的。
那Facade内部不也是这三行代码,不是也就存在这个问题吗
Facade是属于子系统的这一边,本来就应该知道的。
当然了,系统有外观,但是也可以不使用。
外观模式的实现:
1.把外观类当成一个辅助工具类实现,可以定义成单例的。
public class Facade {
private Facade() {}
public static void generate() {
...
...
...
}
}
public class Client {
main() {
Facade.generate();
}
}
2.Facade可以实现成为interface
3.Facade实现成为interface的附带好处
能够有选择性的暴露接口方法,尽量减少模块对子系统外提供的接口方法。
4.Facade的方法实现
Facade的方法实现中,一般是负责把客户端的请求转发给子系统内部的各个模块进行处理,Facade的方法本身并不进行功能的处理,Facade的方法的实现只是实现一个功能的组合调用。
外观模式的优缺点:
1.松散耦合
2.简单易用
3.更好的划分访问的层次
4.过多的或者是不太合理的Facade也容易让人迷惑
外观模式的本质是:封装交互,简化调用;体现了“最少知识原则”
何时选用外观模式:
1.如果你希望为一个复杂的子系统提供一个简单接口的时候,可以考虑使用外观模式,使用外观对象来实现大部分客户需要的功能,从而简化客户的使用
2.如果想要让客户程序和抽象类的实现部分松散耦合,可以考虑使用外观模式,使用外观对象来将这个子系统与它的客户分离开来,从而提高子系统的独立性和可移植性
3.如果构建多层结构的系统,可以考虑使用外观模式,使用外观对象作为每层的入口,这样可以简化层间调用,也可以松散层次之间的依赖关系