门面模式

259 阅读4分钟

门面模式

门面模式(Facade)可能是最简单的结构型设计模式,它能将多个不同的子系统接口封装起来,并对外提供统一的高层接口,使复杂的子系统变得更易使用。顾名思义,“门”可以理解为建筑物的入口,而“面”则通常指物体的外层表面,比如人脸

无论是“门”还是“面”,指代的都是某系统的外观部分,也就是与外界接触的临界面或接口,所以门面模式常常也被翻译为“外观模式”。利用门面模式,我们可以把多个子系统“关”在门里面隐藏起来,成为一个整合在一起的大系统,来自外部的访问只需通过这道“门面”(接口)来进行,而不必再关心门面背后隐藏的子系统及其如何运转。总之,无论门面内部如何错综复杂,从门面外部看来总是一目了然,使用起来也很简单。

一键操作

为了更形象地理解门面模式,我们先来看一个例子。早期的相机使用起来是非常麻烦的,拍照前总是要根据场景情况进行一系列复杂的操作,如对焦、调节闪光灯、调光圈等,非专业人士面对这么一大堆的操作按钮根本无从下手,拍出来的照片质量也不高。随着科技的进步,出现了一种相机,叫作“傻瓜相机”,以形容其使用起来的方便性。用户再也不必学习那些复杂的参数调节了,只要按下快门键就可完成所有操作

化繁为简

在一些商业街区,门面商铺总是聚集在人流量大的地方,而且门头上霓虹闪烁、招牌醒目,访问的便利性使顾客更加愿意购买这些商铺所提供的产品与服务,这也是一个好的门面总能够招揽更多顾客的原因。以餐饮商铺为例,为了享受可口的饭菜与优质的服务,小明决定直接访问这家临街门店。为了达到高效、便捷的目的,门店会统一对子系统进行整合与调度,至于它对蔬菜商、厨师或服务员等子系统是如何操作的,用户都不必了解。下面我们创建外观门面类

Java版本

//Chef.java
package Facade;

public class Chef {
    public void cook(){
        System.out.println("正在做饭");
    }
}
//Waiter.java
package Facade;

public class Waiter {
    public void serve(){
        System.out.println("上菜");
    }
    public void order(){
        System.out.println("下单");
    }
}
//VegVendor.java
package Facade;

public class VegVendor {
    public void purchase(){
        System.out.println("供应蔬菜。。。");
    }
}
//Cleaner.java
package Facade;

public class Cleaner {
    public void clean(){
        System.out.println("收拾桌面");
    }
    public void wash(){
        System.out.println("洗碗");
    }
}
//Facade.java
package Facade;

public class Facade {
    private VegVendor vegVendor;
    private Chef chef;
    private Waiter waiter;
    private Cleaner cleaner;

    public Facade(){
        this.vegVendor = new VegVendor();
        vegVendor.purchase();
        this.chef = new Chef();
        this.waiter = new Waiter();
        this.cleaner  = new Cleaner();
    }

    public void order(){
        //接待入座,点菜
        waiter.order();
        //厨师做饭
        chef.cook();
        //上菜
        waiter.serve();
        //收拾桌子,洗碗等
        cleaner.clean();
        cleaner.wash();
    }
}

Go版本

package facade

import "fmt"

type Chef struct {
}

func (c Chef) Cook() {
    fmt.Println("正在做饭")
}

type Cleaner struct {
}

func (c Cleaner) Clean() {
    fmt.Println("收拾桌面")
}
func (c Cleaner) Wash() {
    fmt.Println("洗碗")
}

type Vegvendor struct {
}

func (v Vegvendor) purcahse() {
    fmt.Println("供应蔬菜")
}

type Waiter struct {
}

func (w Waiter) Serve() {
    fmt.Println("上菜")
}
func (w Waiter) Order() {
    fmt.Println("下单")
}

type Facade struct {
    Vegvendor
    Chef
    Waiter
    Cleaner
}

func NewFacade() Facade {
    facade := Facade{}
    facade.purcahse()
    return facade
}
func (f Facade) Order() {
    f.Waiter.Order()
    f.Cook()
    f.Serve()
    f.Clean()
    f.Wash()
}

外观门面类内部封装了大量的子系统资源,如蔬菜商、厨师、服务员、洗碗工,并于构造方法中依次对各个子系统进行了初始化操作,也就是说餐厅在开门前需要提前准备好这些资源,以便在点菜方法order()中进行依次调度

整合共享

门面模式不但重要,而且其应用也非常广泛,如在软件项目中,我们做多表数据更新时,业务逻辑层(Service层)对数据访问层(DAO层)的调用可能包含多个步骤,除此之外还要进行事务处理,最终统一对外提供一个update()方法,如此一来上层(如控制器Controller层)便可一步调用。软件模块应该只专注于各自擅长的领域,合理明确的分工模式才能更好地整合与共享资源。这正是门面模式所解决的问题,其中外观门面类对子系统的整合与共享极大地保证了用户访问的便利性,作为核心模块,其重要性不言而喻,请参看门面模式的类结构

Facade(外观门面):封装了多个子系统,并将它们整合起来对外提供统一的访问接口。

SubSystemA、SubSystemB、SubSystemC(子系统A、子系统B、子系统C):隐藏于门面中的子系统,数量任意,且对外部不可见。对应本章例程中的蔬菜商类、厨师类、服务员类等。

Client(客户端):门面系统的使用方,只访问门面提供的接口。对客户端这种“门外汉”来说,直接使用子系统是复杂而烦琐的,门面则充当了包装类的角色,对子系统进行整合,再对外暴露统一接口,使其结构内繁外简,最终达到资源共享、简化操作的目的。从另一方面讲,门面模式也降低了客户端与子系统之间的依赖度,高内聚才能低耦合。