故事一
A:听说最近出了某款游戏超好玩,但是我电脑带不起来,需要个组装机玩游戏。
B:你说XX游戏吗?真的很好玩,笔记本玩不了,我就是用的组装机玩的呢。
A:是吗?我对配置这些都不太懂,我得先去研究,然后还需要花时间从CPU、内存、主板、显示器等等这些一家家跑,再回来组装。
B:哪需要这么麻烦啊,你根本不用一个个去跑着买回来组装啊,又耽误时间又浪费精力。你不知道有专门的组装电脑的卖家吗?直接去找他们,把你想要的配置告诉他们就好了,其他你都不用管,他们会把组装好的电脑直接送给你。
A:真的吗?那你有联系方式吗,给我一下,我找他们帮我组装电脑。
过了几天后...
A:哇、小B,太感谢你啦,我上次联系了组装电脑的卖家,把我想要的配置告诉他们以后,他们当天就把组装好的电脑给我送来了,效率太高了,我当天晚上就玩了那款超火的游戏,还带妹子玩的飞起,哈哈!
B:太阳,你又装我,晚上单挑,我虐死你
故事二
A和B都是从外地到杭州打工的青年,A最近刚好搬家到了一个新小区,居住证也在一个星期后到期,需要续签,中午吃饭的时候和B聊到这个事。
B:听说你搬家了?
A:是啊,昨天刚搬,社区周末不上班,还没来得及去登记呢,居住证也快到期了,还要去打印最近半年社保证明,再去派出所办理续签,请一上午假都不知道够不够?
B:我就说你平时就知道打游戏,其他事都不关心,杭州早就实行"最多跑一趟"的服务了,早就不用一个地方一个地方去跑啦,直接去XX派出所就行啦,他们会帮你登记好,住址改签,会帮你打印好社保证明,给你把居住证续签。
A:真的吗?那我只要去派出所就行了,实在太方便了,谢谢你啊!
B:可不是嘛,像什么换驾照啊、车牌啊这些,都不用像以前那样一个地方一个地方跑那么麻烦了,只需要去一个地方,就能一次性把事情办好。我说你平时真的要少玩点游戏,如果想在杭州定居,你要对这些政策多关系一点,以后少跑点弯路。
A:嘿嘿,都被你说的有点不好意思了,难怪你小子都快结婚了,是有原因的啊,哈哈!
B:别别,你是凭实力单身,在下比不了,服!
上面两个都是会经常发生在我们身边的故事,我们从抽象的角度来审视一下:
故事一A作为我们的客户端,电脑城作为我们一个庞大的主系统,cpu、主板、内存这些店作为一个个系统子模块,那么组装电脑的店是什么角色呢?
故事二A依然作为客户端,人口档案管理作为一个庞大的主系统,社区、社保局、银行等作为一个个系统子模块,那么只需要跑一趟的派出所是什么角色呢?
下面,进入我们今天的主题,JAVA设计模式之——外观模式。
什么是外观模式
Facade Pattern: Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.
外观模式(Facade Pattern):为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。
通俗点说,就是通过引入一个外观角色,来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合度,且客户端调用子系统更加方便。正如前言故事中的电脑组装店和一站式服务派出所。他们就是充当了外观角色。
外观模式的两个角色
Facade(外观角色):在客户端可以调用它的方法,在外观角色中可以知道相关的(一个或者多个)子系统的功能和责任;在正常情况下,它将所有从客户端发来的请求委派到相应的子系统去,传递给相应的子系统对象处理。
SubSystem(子系统角色):在软件系统中可以有一个或者多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能;每一个子系统都可以被客户端直接调用,或者被外观角色调用,它处理由外观类传过来的请求;子系统并不知道外观的存在,对于子系统而言,外观角色仅仅是另外一个客户端而已。
外观模式的UML图
外观模式代码实例
我们以轿车为例,以往的汽车,我们用钥匙点火后,白天要自己开日行灯,晚上要手动开大灯,雾天手动开雾灯,冷热也要自己开空调,还有下雨要开雨刮器等等,虽然我们都可以一个个去操作控制,但是高峰期路况太多,很多人反应不过来,这已经不是行车不规范,亲人两行泪能控制的了。
于是万能的人类发明了一键启动按钮,只要按下这个按钮,就会启动Carplay、雨刮器自动感应,空调自动调节、行车灯自动感应功能。这其实也是外观模式的应用,下面我们看看代码实例。
1、定义子系统类Carplay
package com.weiya.mazhichu.designpatterns.facade;
/**
*
* 功能:子系统类:Carplay
*
*
* @author Moore
* @ClassName Car play.
* @Version V1.0.
* @date 2019.07.31 15:21:12
*/
public class CarPlay {
public void openCarPlay(){
System.out.println("carplay开启,可以投影手机导航,或者听歌了");
}
public void closeCarPlay(){
System.out.println("carplay关闭");
}
}
2、定义子系统类空调
package com.weiya.mazhichu.designpatterns.facade;
/**
*
* 功能:子系统类:空调
*
*
* @author Moore
* @ClassName Air conditioning.
* @Version V1.0.
* @date 2019.07.31 15:24:16
*/
public class AirConditioning {
public void openAirConditioningAutoSwitch(){
System.out.println("空调自动调节开启");
}
public void closeAirConditioning(){
System.out.println("空调关闭");
}
}
3、定义子系统类行车灯
package com.weiya.mazhichu.designpatterns.facade;
/**
*
* 功能: 子系统类:行车灯
*
*
* @author Moore
* @ClassName Driving lamp.
* @Version V1.0.
* @date 2019.07.31 15:17:15
*/
public class DrivingLamp {
public void openAutoSwitchLamp(){
System.out.println("行车灯自动感应切换开启");
}
public void turnOffLamp(){
System.out.println("行车灯自动感应关闭");
}
}
4、定义子系统类雨刮器
package com.weiya.mazhichu.designpatterns.facade;
/**
*
* 功能:子系统类:雨刮器
*
*
* @author Moore
* @ClassName Wiper.
* @Version V1.0.
* @date 2019.07.31 15:26:41
*/
public class Wiper {
public void openWiperAutoSensor(){
System.out.println("雨刮器自动感应开启");
}
public void closeWiper(){
System.out.println("雨刮器关闭");
}
}
5、定义外观类(门面类):一键启动按钮
package com.weiya.mazhichu.designpatterns.facade;
/**
*
* 功能:外观类(核心角色):一键启动按钮
*
*
* @author Moore
* @ClassName One click start button.
* @Version V1.0.
* @date 2019.07.31 15:30:51
*/
public class OneClickStartButton {
private CarPlay carPlay;
private AirConditioning airConditioning;
private DrivingLamp drivingLamp;
private Wiper wiper;
public OneClickStartButton() {
this.carPlay = new CarPlay();
this.airConditioning = new AirConditioning();
this.drivingLamp = new DrivingLamp();
this.wiper = new Wiper();
}
/**
*
* 功能:启动
*
*
* @author Moore
* @date 2019.07.31 15:32:15
*/
public void start(){
carPlay.openCarPlay();
airConditioning.openAirConditioningAutoSwitch();
drivingLamp.openAutoSwitchLamp();
wiper.openWiperAutoSensor();
}
/**
*
* 功能:熄火
*
*
* @author Moore
* @date 2019.07.31 15:32:17
*/
public void flameout(){
carPlay.closeCarPlay();
airConditioning.closeAirConditioning();
drivingLamp.turnOffLamp();
wiper.closeWiper();
}
}
6、客户端调用
package com.weiya.mazhichu.designpatterns.facade;
/**
*
* 功能:客户端,直接和外观类(门面)打交道,通过外观类访问子系统
*
*
* @author Moore
* @ClassName Client.
* @Version V1.0.
* @date 2019.07.31 15:36:24
*/
public class Client {
public static void main(String[] args) {
OneClickStartButton button = new OneClickStartButton();
System.out.println("按下一键启动按钮,汽车启动");
button.start();
System.out.println("------------------------");
System.out.println("按下一键启动按钮,汽车熄火");
button.flameout();
}
}
查看运行结果:
通过示例,我们可以看出,客户端和子系统的交互都是通过外观类来进行的,客户端不需要知道子系统内部的实现,只需要跟facade交互即可,降低了客户端和子系统之间的耦合性,同时和客户端交互的对象减少,也使得客户端的代码变得简单。
可以说,外观模式在我们日常生活中随处可见,开发中也形影不离,像mybatis-generator,只需要通过一个外观类,就可以自动生成dao层、mapper层、service层的代码,不需要一级级去生成。还有tomcat、slf4j、spring jdbc等也用到外观模式,这儿不做深入了,感兴趣的同学自己去了解一下。
最后,我们来总结一下外观模式。
优点:
松散耦合:使得客户端和子系统之间解耦,让子系统内部的模块功能更容易扩展和维护。
简单易用:客户端根本不需要知道子系统内部的实现,或者根本不需要知道子系统内部的构成,它只需要跟Facade类交互即可。
更好的划分访问层次:有些方法是对系统外的,有些方法是系统内部相互交互的使用的。子系统把那些暴露给外部的功能集中到门面中,这样就可以实现客户端的使用,很好的隐藏了子系统内部的细节。
缺点:不引入抽象外观类的情况,违背了开闭原则。
适用场景:
对于一个复杂的子系统,需要为用户提供一个简单的交互操作。
不希望客户代码和子系统中的类有耦合,以便提高子系统的独立性和可移植性。
在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。
我不能保证我写的文章都是正确的,但是我能保证都是我自己花时间用心写的,所有的代码示例都是原创,所有的理解都只是我个人理解,不能代表官方权威,所以请各位读者阅读时带着批判的眼光,有选择性的认同,谢谢!