设计模式之外观模式

1,204 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

在家里吃饭,需要买菜、烧菜、洗碗等等一系列过程,甚为繁琐。但是去饭店吃饭,只需要跟服务员点好菜就可以了,剩下的一系列工作都由饭店的工作人员来完成,非常简单省事。

在业务系统开发中也是这样,通常一个较复杂的功能都是需要和若干个业务类进行交互,在一些场景中,这些交互总是批量出现的,这就会导致使用时较为复杂,出现较多重复的代码。这时,如果有一个类似服务员的角色,由它来负责和复杂的类或者业务进行交互,而使用这些业务的类只需要和它交互就可以了。外观模式就是充当了这么一个角色,它为多个业务类的调用提供统一的入口,简化了类与类之间的交互。

业务设计

公司开发的商城系统中,下订单这个过程是一个复杂的过程,它包含3个部分,分别是扣费用,减库存,加发货单。为了符合单一职责原则,这三个过程分别在三个不同的类或服务里,这就导致每个平台在下订单时都会涉及这一系列动作。

image.png

可以看出,该方案主要有2个问题

  1. 三个子过程基本都是以整体的形式出现,但是在各个平台里却需要与他们逐个交互,导致很多重复代码
  2. 如果其中一个子过程变更,比如双十一了,扣费用服务需要变更新的服务,则需要每个平台都更换一次代码,系统维护困难,灵活性和可扩展性比较差

为了解决这两个问题,可以在平台和子过程之间引入一个外观类,由它来封装子过程的交互。

image.png

示例代码

首先建立三个子过程类,这里简单模拟

//费用服务
@Service
public class Money {
    public void deduct(){
        System.out.println("扣费用");
    }
}
//库存服务
@Service
public class Inventory {
    public void reduce(){
        System.out.println("减库存");
    }
}
//发货服务
@Service
public class Delivery {
    public void add(){
        System.out.println("加发货单");
    }
}

新建一个外观类,将三个子过程类注入进来,新建一个makeOrder方法将子过程组合起来

//外观类
@Component
public class OrderFacade {

    @Autowired
    private Money money;

    @Autowired
    private Delivery delivery;

    @Autowired
    private Inventory inventory;

    public void makeOrder(){
        money.deduct();
        delivery.add();
        inventory.reduce();
    }
}

在平台客户端注入外观类,调用外观类的makeOrder方法

//平台客户端
@Autowired
private OrderFacade orderFacade;

@Test
public void  Order(){
    orderFacade.makeOrder();
}

但是很容易能看出来一个问题,如果我的子过程变更了,虽然不用修改客户端类了,但是还是要修改外观类,虽然这个工作量不大,从这一方面来说,它是不满足开闭原则的。

抽象外观类

为了解决上面的问题,可以通过引入抽象外观类的方式。它可以是一个抽象类,也可以是一个接口,这里以接口为示例:

//抽象外观接口
public interface AbstractFacade {
    public void  makeOrder();
}

让原来的外观类继承这个接口,并给他命名OrderFacade

@Service("OrderFacade")
public class OrderFacade implements AbstractFacade{

    @Autowired
    private Money money;

    @Autowired
    private Delivery delivery;

    @Autowired
    private Inventory inventory;

    @Override
    public void makeOrder(){
        money.deduct();
        delivery.add();
        inventory.reduce();
    }
}

在客户端根据情况,使用不同的实现类即可

@Resource(name="OrderFacade")
private AbstractFacade orderFacade;

@Test
public void  Order(){
    orderFacade.makeOrder();
}

这样,有不同的外观需求,只需要通过扩展AbstractFacade的子类即可,非常的方便

总结

外观模式其实是一种使用非常频繁的设计模式,相信各个同学编码的过程中虽然实现方式不同,但都或多或少有着它的影子,它为复杂系统提供统一的入口,使子系统与客户端耦合度降低,简化接口调用。它不给系统增加功能,只是简化调用的过程。

当你在项目中为复杂系统提供简单入口,或者希望客户端与子系统之间解耦时,都可以考虑使用外观模式