案例
组建一个家庭影院:
DVD播放器,投影仪,自动屏幕,环绕立体声,爆米花机,灯光,要求完成使用家庭影院的功能,其过程为:
- 直接用遥控器统筹各设备开关
- 开爆米花机
- 放下屏幕
- 开投影仪
- 开音响
- 开DVD,选dvd
- 去拿爆米花
- 调暗灯光
- 播放
- 观影结束后,关闭各种设备
传统解决方案
客户端直接创建并调用各子系统的相关功能方法
传统解决方案带来的问题
在客户端直接调用子系统的方法,会造成调用过程混乱,没有清晰的过程,同时客户端过多的关注子系统的内部实现,使得客户端调用子系统的难度升高,不利于维护对子系统的操作
使用外观模式解决问题
HomeTheaterFacade:外观类,定义了客户端和各子系统之间的一个高层调用接口界面,给子系统中的一组接口提供一个一致的界面(ready,end,play,pause),用来统一调度子系统中的一群接口,以屏蔽内部子系统的细节,使得客户端只需跟这个接口发送调用。而无需关心这个子系统的内部细节
/**
*
* @ClassName FacadePattern
* @Description 外观模式
* @author fuling
* @date 2020年11月15日 上午10:57:09
*/
public class FacadePattern {
public static void main(String[] args) {
//创建一个家庭影院遥控器外观类
HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();
System.out.println("观影前准备");
homeTheaterFacade.ready();
System.out.println("=============================");
System.out.println("开始观影");
homeTheaterFacade.play();
System.out.println("=============================");
System.out.println("暂停观影");
homeTheaterFacade.pause();
System.out.println("=============================");
System.out.println("结束观影");
homeTheaterFacade.end();
}
}
/**
*
* @ClassName HomeTheaterFacade
* @Description 外观类(家庭影院遥控器)
* @author fuling
* @date 2020年11月15日 上午10:57:57
*/
class HomeTheaterFacade{
//组合了家庭影院的各种设备
DVDPlayer dvdPlayer = new DVDPlayer();
Projector projector = new Projector();
Stereo stereo = new Stereo();
Popcorn popcorn = new Popcorn();
Screen screen = new Screen();
Light light = new Light();
//观影前的准备工作
void ready() {
//开爆米花机
popcorn.on();
//放下屏幕
screen.down();
//开投影仪
projector.on();
//开音响
stereo.on();
//开DVD,选dvd
dvdPlayer.on();
dvdPlayer.setDVD();
//去拿爆米花
popcorn.pop();
//调暗灯光
light.dim();
}
//观影结束的后的处理
void end() {
//调亮灯光
light.bright();
//关掉DVD
dvdPlayer.off();
//关掉音响
stereo.off();
//关闭投影仪
projector.off();
//上升屏幕
screen.up();
//关闭爆米花机
popcorn.off();
}
//开始观影
void play() {
dvdPlayer.play();
}
//暂停观影
void pause() {
dvdPlayer.pause();
}
}
/**
*
* @ClassName DVDPlayer
* @Description DVD播放器
* @author fuling
* @date 2020年11月15日 上午11:03:18
*/
class DVDPlayer{
//打开DVD播放器
void on() {
System.out.println("打开DVD播放器");
}
//关闭DVD播放器
void off() {
System.out.println("关闭DVD播放器");
}
//开始播放
void play() {
System.out.println("开始播放");
}
//暂停播放
void pause() {
System.out.println("暂停播放");
}
//选择dvd
void setDVD() {
System.out.println("选择dvd");
}
}
/**
*
* @ClassName Projector
* @Description 投影仪
* @author fuling
* @date 2020年11月15日 上午11:07:02
*/
class Projector{
//打开投影仪
void on() {
System.out.println("打开投影仪");
}
//关闭投影仪
void off() {
System.out.println("关闭投影仪");
}
//聚焦投影仪
void focus() {
System.out.println("聚焦投影仪");
}
//移动投影仪位置
void zoom() {
System.out.println("移动投影仪位置");
}
}
/**
*
* @ClassName Stereo
* @Description 环绕立体声系统
* @author fuling
* @date 2020年11月15日 上午11:09:02
*/
class Stereo{
//打开环绕立体声
void on() {
System.out.println("打开环绕立体声");
}
//关闭环绕立体声
void off() {
System.out.println("关闭环绕立体声");
}
//调节声音
void setVolume(int volume) {
System.out.println("调节声音到" + volume);
}
}
/**
*
* @ClassName Popcorn
* @Description 爆米花机
* @author fuling
* @date 2020年11月15日 上午11:11:15
*/
class Popcorn{
//打开爆米花机
void on() {
System.out.println("打开爆米花机");
}
//关闭爆米花机
void off() {
System.out.println("关闭爆米花机");
}
//做爆米花
void pop(){
System.out.println("拿爆米花");
}
}
/**
*
* @ClassName Screen
* @Description 自动屏幕
* @author fuling
* @date 2020年11月15日 上午11:18:12
*/
class Screen{
//上升屏幕
void up() {
System.out.println("上升屏幕");
}
//放下屏幕
void down() {
System.out.println("放下屏幕");
}
}
/**
*
* @ClassName Light
* @Description 影院灯光
* @author fuling
* @date 2020年11月15日 上午11:20:19
*/
class Light{
//开灯
void on() {
System.out.println("开灯");
}
//关灯
void off() {
System.out.println("关灯");
}
//调暗灯光
void dim() {
System.out.println("调暗灯光");
}
//调亮灯光
void bright() {
System.out.println("调亮灯光");
}
}
外观模式小结
外观类(Facade):为调用端提供统一的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象
调用者(Client):外观接口的调用者
子系统的集合:指模块或者子系统,处理外观对象指派的任务,它是功能的实际提供者
- 外观模式可以理解为转换一群接口,客户只要调用一个接口,而不用调用多个接口才能达到目的。比如在pc上安装软件时经常有一键安装选项
- 外观模式就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用
- 外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端使用子系统的复杂性
- 外观模式降低了客户端与子系统的耦合关系,让子系统内部的模块更易维护和扩展
- 合理的使用外观模式,可以帮我们更好的划分访问层次,当系统需要进行分层设计时,可以考虑使用该模式
- 在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为这些遗留的系统开发一个外观类,封装对这些遗留系统的调用细节,为新系统提供一个统一调用的接口(让新系统直接与外观类交互),提高复用性
- 不能过多的或者不合理的使用外观模式,使用外观模式还是直接调用模块,要以让系统有层次,利于维护为目的,如果系统很简单,那么就可以直接调用模块
外观模式在mybatis框架中的应用
//外观类
public class Configuration {
//组合了三个工厂模块
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
protected ObjectFactory objectFactory = new DefaultObjectFactory();
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
//对外的统一接口,将三种工厂模块传到MetaObject.forObject中,由MetaObject.forObject决定如何调用三种工厂模块的方法
public MetaObject newMetaObject(Object object) {
return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
public class MetaObject {
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
if (object == null) {
return SystemMetaObject.NULL_META_OBJECT;
} else {
return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
if (object instanceof ObjectWrapper) {
this.objectWrapper = (ObjectWrapper) object;
} else if (objectWrapperFactory.hasWrapperFor(object)) {
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
} else if (object instanceof Map) {
this.objectWrapper = new MapWrapper(this, (Map) object);
} else if (object instanceof Collection) {
this.objectWrapper = new CollectionWrapper(this, (Collection) object);
} else {
this.objectWrapper = new BeanWrapper(this, object);
}
}
}