前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >门面模式浅析

门面模式浅析

作者头像
孟君
发布2019-09-17 15:14:21
5200
发布2019-09-17 15:14:21
举报

在生活中,比如在医院有接待员帮助病人完成门诊、挂号、付费以及取药,病人只接触接待员即可,由接待员负责与医院的各个部门打交道。

再如,诸多交易场景,也是需要一个客户服务的门面,完成诸如订单、支付和航运相关的服务。

类似这些场景,就是我们今天要聊的门面模式可以做的事情。

一、门面模式基本介绍

1.1 意图

门面模式是一种对象结构型模式,其意图是为子系统中的一组接口提供一个一致的界面Facade模式定义了一个高层接口这个接口使得这一子系统更加容易使用

1.2 结构

门面模式的基本结构如下:

  • Facade
    • 知道哪些子系统类负责处理请求
    • 将客户的请求代理给适当的子系统对象
  • Subsystem class
    • 实现子系统的功能
    • 处理由Facade对象指派的任务
    • 没有facade的任何相关信息;即没有指向facade的指针

二、为什么要使用Facade

随着系统越来越复杂,一般会划分成若干个子系统降低系统的复杂性。如果每个客户需要与一个或者多个子系统进行交互,势必会增加耦合性。

接下来我们模拟一个系统,其中子系统有产品服务、设备服务、账号服务等,多个业务场景需要调用到产品服务、设备服务等。如:

2.1 是是

2.1 不使用Facade

在这种情况下,业务系统直接各个子系统进行交互。简单的代码如下:

  • 子系统相关服务ProductService和DeviceService
代码语言:javascript
复制
public interface ProductService {

  ProductDTO findByProductKey(String productKey);
}
代码语言:javascript
复制
public interface DeviceService {

  Long countByProductKey(String productKey);
  
}

ProductService和DeviceService可以是微服务,如Dubbo。

代码语言:javascript
复制
public class ProductServiceImpl implements  ProductService {

  private ProductDTO mockProductDTO() {
    ProductDTO productDTO = new ProductDTO();
    productDTO.setName("Product 1");
    productDTO.setProductKey("1234567890");
    return productDTO;
  }

  public ProductDTO findByProductKey(String productKey) {
    return mockProductDTO();
  }

}
代码语言:javascript
复制

public class DeviceServiceImpl implements DeviceService {

  public Long countByProductKey(String productKey) {
    return 100L;
  }

}
  • 使用到的DTO类

ProductDTO - 简单的产品信息

代码语言:javascript
复制
import java.io.Serializable;

public class ProductDTO implements Serializable {

  private static final long serialVersionUID = 2571209854790235084L;

  private String productKey;
  
  private String name;


  /**
   * @return the productKey
   */
  public String getProductKey() {
    return productKey;
  }

  /**
   * @param productKey the productKey to set
   */
  public void setProductKey(String productKey) {
    this.productKey = productKey;
  }

  /**
   * @return the name
   */
  public String getName() {
    return name;
  }

  /**
   * @param name the name to set
   */
  public void setName(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return "ProductDTO [productKey=" + productKey + ", name=" + name + ", getProductKey()=" + getProductKey()
        + ", getName()=" + getName() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode()
        + ", toString()=" + super.toString() + "]";
  }
  
  
}

DeviceDTO - 简单的设备信息

代码语言:javascript
复制
import java.io.Serializable;

public class DeviceDTO implements Serializable  {

  private static final long serialVersionUID = -5464821636761682860L;
  
  private String name;
  
  private String productKey;
  
  private String accountId;

  /**
   * @return the name
   */
  public String getName() {
    return name;
  }

  /**
   * @param name the name to set
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * @return the productKey
   */
  public String getProductKey() {
    return productKey;
  }

  /**
   * @param productKey the productKey to set
   */
  public void setProductKey(String productKey) {
    this.productKey = productKey;
  }

  /**
   * @return the accountId
   */
  public String getAccountId() {
    return accountId;
  }

  /**
   * @param accountId the accountId to set
   */
  public void setAccountId(String accountId) {
    this.accountId = accountId;
  }

  @Override
  public String toString() {
    return "DeviceDTO [name=" + name + ", productKey=" + productKey + ", accountId=" + accountId + "]";
  }
  
  

}

ProductDetailDTO - 包含产品设备数的产品信息

代码语言:javascript
复制
import java.io.Serializable;

public class ProductDetailDTO implements Serializable {

  private static final long serialVersionUID = 9105918059993472221L;

  private String productKey;
  
  private String name;
  
  private Long deviceCount;

  /**
   * @return the productKey
   */
  public String getProductKey() {
    return productKey;
  }

  /**
   * @param productKey the productKey to set
   */
  public void setProductKey(String productKey) {
    this.productKey = productKey;
  }

  /**
   * @return the name
   */
  public String getName() {
    return name;
  }

  /**
   * @param name the name to set
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * @return the deviceCount
   */
  public Long getDeviceCount() {
    return deviceCount;
  }

  /**
   * @param deviceCount the deviceCount to set
   */
  public void setDeviceCount(Long deviceCount) {
    this.deviceCount = deviceCount;
  }

  @Override
  public String toString() {
    return "ProductDetailDTO [productKey=" + productKey + ", name=" + name + ", deviceCount=" + deviceCount + "]";
  }
  
  
}
  • 业务场景调用

业务A

代码语言:javascript
复制

public class BusinessA {

private ProductService productService;
  
  private DeviceService deviceService;
  
  public ProductDetailDTO findByProductKey(String productKey) {
    ProductDTO productDTO = productService.findByProductKey(productKey);
    Long deviceCount = deviceService.countByProductKey(productKey);
    /**
     * 将productDTO和deviceCount拼成ProductDetailDTO信息
     */
    ProductDetailDTO productDetailDTO =  new ProductDetailDTO();
    productDetailDTO.setDeviceCount(deviceCount);
    productDetailDTO.setName(productDTO.getName());
    productDetailDTO.setProductKey(productDTO.getProductKey());
    return productDetailDTO;
  }


  /**
   * @param productService the productService to set
   */
  public void setProductService(ProductService productService) {
    this.productService = productService;
  }


  /**
   * @param deviceService the deviceService to set
   */
  public void setDeviceService(DeviceService deviceService) {
    this.deviceService = deviceService;
  }

}

业务B

代码语言:javascript
复制

public class BusinessB {

private ProductService productService;
  
  private DeviceService deviceService;
  
  public ProductDetailDTO findByProductKey(String productKey) {
    ProductDTO productDTO = productService.findByProductKey(productKey);
    Long deviceCount = deviceService.countByProductKey(productKey);
    /**
     * 将productDTO和deviceCount拼成ProductDetailDTO信息
     */
    ProductDetailDTO productDetailDTO =  new ProductDetailDTO();
    productDetailDTO.setDeviceCount(deviceCount);
    productDetailDTO.setName(productDTO.getName());
    productDetailDTO.setProductKey(productDTO.getProductKey());
    return productDetailDTO;
  }


  /**
   * @param productService the productService to set
   */
  public void setProductService(ProductService productService) {
    this.productService = productService;
  }


  /**
   * @param deviceService the deviceService to set
   */
  public void setDeviceService(DeviceService deviceService) {
    this.deviceService = deviceService;
  }

}

如果采用上述方式来处理,那么我们可以看到如下的问题

  • 每个业务系统都需要包含一个或者多个子系统的服务,如上述示例中的ProductService和DeviceService。
  • 子系统中包含的服务部分可能只在内部子系统间调用,而无需让用户知道,业务系统对全部服务可见,可能导致调用不恰当,引入风险。现实中,很多业务方会想当然的觉得某个服务的作用,而随意调用。
  • 如果子系统服务升级,可能导致业务系统对多个子系统服务的升级。业务方需要维护多个子系统版本。

为了解决上述问题,可以引入一个Facade。

2.2 使用Facade

  • Facade相关服务
代码语言:javascript
复制

public interface ProductFacade {

  ProductDetailDTO findByProductKey(String productKey);
  
}

ProductFacade的实现,其调用子系统的接口,然后提供服务。

代码语言:javascript
复制

public class ProductFacadeImpl implements ProductFacade {

  private ProductService productService;
  
  private DeviceService deviceService;
  
  public ProductDetailDTO findByProductKey(String productKey) {
    ProductDTO productDTO = productService.findByProductKey(productKey);
    Long deviceCount = deviceService.countByProductKey(productKey);
    /**
     * 将productDTO和deviceCount拼成ProductDetailDTO信息
     */
    ProductDetailDTO productDetailDTO =  new ProductDetailDTO();
    productDetailDTO.setDeviceCount(deviceCount);
    productDetailDTO.setName(productDTO.getName());
    productDetailDTO.setProductKey(productDTO.getProductKey());
    return productDetailDTO;
  }

  /**
   * @return the productService
   */
  public ProductService getProductService() {
    return productService;
  }

  /**
   * @param productService the productService to set
   */
  public void setProductService(ProductService productService) {
    this.productService = productService;
  }

  /**
   * @return the deviceService
   */
  public DeviceService getDeviceService() {
    return deviceService;
  }

  /**
   * @param deviceService the deviceService to set
   */
  public void setDeviceService(DeviceService deviceService) {
    this.deviceService = deviceService;
  }

  
}
  • 业务调用

有了Facade,业务方只需要和Facade进行交互,与各个子系统是没有直接的关联关系的,这样,业务方只要维护一个Facade的版本即可。

业务A和业务B调整后如下,其只要和Facade进行交互即可。Facade可以完成和子系统的交互。

代码语言:javascript
复制

public class NewBusinessA {

  private ProductFacade productFacade;
  
  public ProductDetailDTO findByProductKey(String productKey) {
    return productFacade.findByProductKey(productKey);
  }

  /**
   * @param productFacade the productFacade to set
   */
  public void setProductFacade(ProductFacade productFacade) {
    this.productFacade = productFacade;
  }
  

}
代码语言:javascript
复制

public class NewBusinessB {

  private ProductFacade productFacade;
  
  public ProductDetailDTO findByProductKey(String productKey) {
    return productFacade.findByProductKey(productKey);
  }

  /**
   * @param productFacade the productFacade to set
   */
  public void setProductFacade(ProductFacade productFacade) {
    this.productFacade = productFacade;
  }
  

}

这样,一个简单的Facade示例就完成了。

三、小结

最后,我们再来做一个小结。

  • 子系统内部直接往往存在着一定的关联的。使用了Facade,可以降低客户端和子系统之间的耦合。客户端无需关注子系统内部的关系和细节。提高了子系统的独立性和可移植性。
  • 引入Facade后,客户端只要维护Facade的版本,而不需要维护各个子系统的版本,维护和升级变的简洁。
  • Facade的抽象可以有多个,比如细化的话,上述示例可以有ProductFacade、DeviceFacade等等。Facade提供的服务其实是通过调用子系统服务来协作完成的。Facade提供给用户的服务是客户需要的,而不是全部的子系统的能力。
  • 引入Facade也符合迪米特法则。迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。英文简写为: LoD。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 孟君的编程札记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档