外观模式详解

外观模式(Facade Pattern)

结构型设计模式

1. 定义

外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

通俗点说,就是有很多子系统(或类或模块),它们各自有各自的功能,将它们的部分或全部功能按一定的流程去执行,就组合成一个完整的客户需要的功能,但是,客户一个个去执行这些功能,会费时费力,所以我们提供一个类(或模块或系统),把这些执行流程封装起来,封装成一个接口(方法),暴露给客户去使用。

2. 角色
  1. 外观角色(Facade),也叫门面角色,外观模式的核心。它被客户角色调用,它熟悉子系统的功能,内部根据客户角色的需求制定了几种功能的组合
  2. 子系统角色(Subsystem),许多系统的集合,一个系统提供一系列功能,功能间可能存在依赖关系,也可能毫无关系;子系统是被外观角色整合的目标
  3. 客户角色(Client),也就是客户端,子系统的使用者,通过外观角色进行间接调用(高大尚的说法叫解耦,客户不需要了解子系统)
3. 优点
  1. 解耦:客户端和子系统之间解耦
  2. 封装变化:子系统内部扩展和维护不会影响到客户,影响到的是外观角色
  3. 简化使用:对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易
  4. 只是提供了一个访问子系统的统一入口,但并不影响用户直接使用子系统

这里涉及到一个设计原则:最少知识原则(客户只了解Facade类)

4. 缺点

不符合开闭原则,增加子系统时,需要直接对Facade类动刀

5. 何时使用
  1. 当一个功能涉及到众多子系统时,就可以把这些执行流程封装成一个接口
  2. 层次化结构,如controller-service-dao,一个controller可以封装多个service,一个service可以封装多个dao
6. 生活中的例子
  1. 餐饮:客户只需要跟服务员打交道(点菜),就可以得到菜肴,不需要种菜、摘菜、洗菜、切菜、炒菜等等
  2. 基金、各种中介、电脑开关机、等等,太多了

反正提供服务的,差不多都有外观角色的思想

7. 与适配器模式的区别
  1. 适配器模式重点在于,将一个接口,转换到客户需要的接口
  2. 外观模式的重点在于,将一系列接口(小功能),组装成一个或多个接口(客户需要的功能)
  3. 在适配器模式中,客户不能直接调用待转换的接口
  4. 在外观模式中,客户可以直接调用待组装的接口
8. 例子

例子仅仅为了举例

一个电商系统,有很多子系统:商品系统、库存系统、订单系统、用户系统,等等

每个系统有很多功能,这里,仅编写增删改查,和例子需要用到的功能

客户端需要两个功能(当然不止两个)

  1. 下单:查询商品信息->查询商品库存->减少商品库存->查询用户信息-保存用户轨迹->生成订单
  2. 取消订单:修改订单状态-增加商品库存-保存用户轨迹

这两个功能都需要使用到各个子系统的部分功能

8.1 当没有用外观模式时,是这样的

商品系统

// 商品系统
public class GoodsSystem {

    public void addGoods() {
        System.out.println("增加商品");
    }

    public void deleteGoods() {
        System.out.println("删除商品");
    }

    public void updateGoods() {
        System.out.println("修改商品");
    }

    public void selectGoods() {
        System.out.println("查询商品");
    }
}

库存系统

// 库存系统
public class SKUSystem {

    public void addSKU() {
        System.out.println("增加库存");
    }

    public void deleteSKU() {
        System.out.println("删除库存");
    }

    public void updateSKU() {
        System.out.println("修改库存");
    }

    public void selectSKU() {
        System.out.println("查询库存");
    }

    public void subtractSKUCount() {
        System.out.println("减少商品存量");
    }

    public void addSKUCount() {
        System.out.println("增加商品存量");
    }
}

订单系统

// 订单系统
public class OrderSystem {

    public void addOrder() {
        System.out.println("增加订单");
    }

    public void deleteOrder() {
        System.out.println("删除订单");
    }

    public void updateOrder() {
        System.out.println("修改订单");
    }

    public void selectOrder() {
        System.out.println("查询订单");
    }
}

用户系统

// 用户系统
public class UserSystem {

    public void addUser() {
        System.out.println("增加用户");
    }

    public void deleteUser() {
        System.out.println("删除用户");
    }

    public void updateUser() {
        System.out.println("修改用户");
    }

    public void selectUser() {
        System.out.println("查询用户");
    }

    public void addUserPath() {
        System.out.println("保存用户轨迹");
    }
}

客户端,客户下单,取消订单

// 客户端
public class Client {

    public static void main(String[] args) {

        // 下单:查询商品信息->查询商品库存->减少商品库存->查询用户信息-保存用户轨迹->生成订单
        System.out.println("客户下单...");

        GoodsSystem goodsSystem = new GoodsSystem();
        goodsSystem.selectGoods(); // 查询商品信息

        SKUSystem skuSystem = new SKUSystem();
        skuSystem.selectSKU(); // 查询商品库存

        skuSystem.subtractSKUCount(); // 减少商品库存

        UserSystem userSystem = new UserSystem();
        userSystem.selectUser(); // 查询用户信息

        userSystem.addUserPath(); // 保存用户轨迹

        OrderSystem orderSystem = new OrderSystem();
        orderSystem.addOrder(); // 生成订单

        System.out.println("---------");

        // 取消订单:修改订单状态-增加商品库存-保存用户轨迹
        System.out.println("客户取消订单...");

        orderSystem.updateOrder(); // 修改订单状态

        skuSystem.addSKUCount(); // 增加商品库存

        userSystem.addUserPath(); // 保存用户轨迹

    }
}

输出

客户下单...
查询商品
查询库存
减少商品存量
查询用户
保存用户轨迹
增加订单
---------
客户取消订单...
修改订单
增加商品存量
保存用户轨迹

结论

  1. 客户需要的操作多而复杂,也容易出错
  2. 客户与各个子系统直接交互,耦合度高
8.2 如果使用外观模式的话

现有代码不需要做修改,只增加一个类(外观角色)

// 外观角色
public class Facade {
    private GoodsSystem goodsSystem;
    private OrderSystem orderSystem;
    private SKUSystem skuSystem;
    private UserSystem userSystem;

    public Facade() {
        goodsSystem = new GoodsSystem();
        orderSystem = new OrderSystem();
        skuSystem = new SKUSystem();
        userSystem = new UserSystem();
    }

    // 下单
    public void placeOrder() {

        // 下单:查询商品信息->查询商品库存->减少商品库存->查询用户信息-保存用户轨迹->生成订单
        System.out.println("客户下单...");

        goodsSystem.selectGoods(); // 查询商品信息

        skuSystem.selectSKU(); // 查询商品库存

        skuSystem.subtractSKUCount(); // 减少商品库存

        userSystem.selectUser(); // 查询用户信息

        userSystem.addUserPath(); // 保存用户轨迹

        orderSystem.addOrder(); // 生成订单
    }

    // 取消订单
    public void cancelOrder() {

        // 取消订单:修改订单状态-增加商品库存-保存用户轨迹
        System.out.println("客户取消订单...");

        orderSystem.updateOrder(); // 修改订单状态

        skuSystem.addSKUCount(); // 增加商品库存

        userSystem.addUserPath(); // 保存用户轨迹
    }
}

客户端

// 客户端
public class Client {

    public static void main(String[] args) {

        Facade facade = new Facade(); // 创建外观角色,让他去跟子系统打交道

        facade.placeOrder(); // 下单

        facade.cancelOrder(); // 取消订单

    }
}

输出

客户下单...
查询商品
查询库存
减少商品存量
查询用户
保存用户轨迹
增加订单
---------
客户取消订单...
修改订单
增加商品存量
保存用户轨迹

结论

  1. 客户使用起来就方便多了,也不用担心出错
  2. 客户与子系统解耦了,不必知道子系统的内部实现,只需要跟外观角色打交道就好
  3. 后续子系统的升级修改,只对外观角色暴露,不会影响到客户

注意

  1. 并不是说子系统封装后,客户就只能通过外观角色去使用,外观只是提供一个简化的使用方式
  2. 客户也还是可以直接调用子系统的,并且,子系统的功能相当多,并不只是为了给“下单”“取消订单”这两个功能服务的

如买家上线商品,客户搜索商品等等,都需要使用到子系统的部分功能

  1. 子系统的功能,可能有依赖关系,也可能没有依赖关系

如没有库存,就不能购买了,这就是依赖;如保存用户轨迹,这个就是独立的功能,与其它子系统没有必然联系

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值