Java设计模式系列之——外观模式

故事一

A:听说最近出了某款游戏超好玩,但是我电脑带不起来,需要个组装机玩游戏。

B:你说XX游戏吗?真的很好玩,笔记本玩不了,我就是用的组装机玩的呢。

A:是吗?我对配置这些都不太懂,我得先去研究,然后还需要花时间从CPU、内存、主板、显示器等等这些一家家跑,再回来组装。

B:哪需要这么麻烦啊,你根本不用一个个去跑着买回来组装啊,又耽误时间又浪费精力。你不知道有专门的组装电脑的卖家吗?直接去找他们,把你想要的配置告诉他们就好了,其他你都不用管,他们会把组装好的电脑直接送给你。

A:真的吗?那你有联系方式吗,给我一下,我找他们帮我组装电脑。

过了几天后...

A:哇、小B,太感谢你啦,我上次联系了组装电脑的卖家,把我想要的配置告诉他们以后,他们当天就把组装好的电脑给我送来了,效率太高了,我当天晚上就玩了那款超火的游戏,还带妹子玩的飞起,哈哈!

B:太阳,你又装我,晚上单挑,我虐死你

故事二

A和B都是从外地到杭州打工的青年,A最近刚好搬家到了一个新小区,居住证也在一个星期后到期,需要续签,中午吃饭的时候和B聊到这个事。

B:听说你搬家了?

A:是啊,昨天刚搬,社区周末不上班,还没来得及去登记呢,居住证也快到期了,还要去打印最近半年社保证明,再去派出所办理续签,请一上午假都不知道够不够?

B:我就说你平时就知道打游戏,其他事都不关心,杭州早就实行"最多跑一趟"的服务了,早就不用一个地方一个地方去跑啦,直接去XX派出所就行啦,他们会帮你登记好,住址改签,会帮你打印好社保证明,给你把居住证续签。

A:真的吗?那我只要去派出所就行了,实在太方便了,谢谢你啊!

B:可不是嘛,像什么换驾照啊、车牌啊这些,都不用像以前那样一个地方一个地方跑那么麻烦了,只需要去一个地方,就能一次性把事情办好。我说你平时真的要少玩点游戏,如果想在杭州定居,你要对这些政策多关系一点,以后少跑点弯路。

A:嘿嘿,都被你说的有点不好意思了,难怪你小子都快结婚了,是有原因的啊,哈哈!

B:别别,你是凭实力单身,在下比不了,服!

上面两个都是会经常发生在我们身边的故事,我们从抽象的角度来审视一下:

  1. 故事一A作为我们的客户端,电脑城作为我们一个庞大的主系统,cpu、主板、内存这些店作为一个个系统子模块,那么组装电脑的店是什么角色呢?

  2. 故事二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;

/**
 * <p class="detail">
 * 功能:子系统类:Carplay
 * </p>
 *
 * @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;

/**
 * <p class="detail">
 * 功能:子系统类:空调
 * </p>
 *
 * @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;

/**
 * <p class="detail">
 * 功能: 子系统类:行车灯
 * </p>
 *
 * @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;

/**
 * <p class="detail">
 * 功能:子系统类:雨刮器
 * </p>
 *
 * @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;

/**
 * <p class="detail">
 * 功能:外观类(核心角色):一键启动按钮
 * </p>
 *
 * @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();
    }

    /**
     * <p class="detail">
     * 功能:启动
     * </p>
     *
     * @author Moore
     * @date 2019.07.31 15:32:15
     */
    public void start(){
        carPlay.openCarPlay();
        airConditioning.openAirConditioningAutoSwitch();
        drivingLamp.openAutoSwitchLamp();
        wiper.openWiperAutoSensor();
    }

    /**
     * <p class="detail">
     * 功能:熄火
     * </p>
     *
     * @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;

/**
 * <p class="detail">
 * 功能:客户端,直接和外观类(门面)打交道,通过外观类访问子系统
 * </p>
 *
 * @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类交互即可。

  • 更好的划分访问层次:有些方法是对系统外的,有些方法是系统内部相互交互的使用的。子系统把那些暴露给外部的功能集中到门面中,这样就可以实现客户端的使用,很好的隐藏了子系统内部的细节。

缺点:不引入抽象外观类的情况,违背了开闭原则。

适用场景:

  • 对于一个复杂的子系统,需要为用户提供一个简单的交互操作。

  • 不希望客户代码和子系统中的类有耦合,以便提高子系统的独立性和可移植性。

  • 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。

 

我不能保证我写的文章都是正确的,但是我能保证都是我自己花时间用心写的,所有的代码示例都是原创,所有的理解都只是我个人理解,不能代表官方权威,所以请各位读者阅读时带着批判的眼光,有选择性的认同,谢谢!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章