门面或装饰

Sik*_*ski 5 java design-patterns facade decorator

背景:

我有一个REST服务让我们说CustomerService现在有一个方法getCustomer(id, country).现在的要求是,根据国家/地区,我必须执行不同的业务逻辑,例如访问不同的数据库或一些自定义规则,然后报告我收到了这样的请求.

首先根据国家我使用Factory模式解决不同的实现,如下所示:

所有国家/地区实施的通用接口

public Interface CustomerServiceHandler{
   Cusomer getCustomer(String id, String country);
}
Run Code Online (Sandbox Code Playgroud)

然后工厂作为

public class CustomerServiceHandlerFactory{
   public CustomerServiceHandler getHandler(String country){...};
}
Run Code Online (Sandbox Code Playgroud)

使用Facade实现详细信息

请注意,此外观是从REST类调用的,即CustomerService

public CustomerServiceFacade{
   public Customer getCustomer(String id, String country){
        //use factory to get handler and then handler.getCustomer
        Customer customer = factory.getHandler(country).getCustomer(id,country);
        //report the request
        reportingService.report('fetch-customer',....);
        return customer;
   }
}
Run Code Online (Sandbox Code Playgroud)

按照SRP(单一责任原则),这个立面并没有实现单一目标.它正在抓住客户以及报告收到此类请求.所以我想到装饰模式如下.

使用Decorator模式实现:

//this is called from Rest layer
public ReportingCustomerHandler implements CustomerServiceHandler{
    //this delegate is basically the default implementation and has factory too                    
    private CustomerServiceHandler delegate;
    private ReportingService reporting;
    public Customer getCustomer(String id, String country){
           Customer customer = delegate.getCustomer(id, country);
           reporting.report(....);
           return customer;
    }
}


 //this is called from ReportingCustomerHandler           
 public DefaultCustomerServiceHandler implements CustomerServiceHandler{
     private CustomerServiceHandlerFactory factory;                    

     public Customer getCustomer(String id, String country){
         //get factory object else use itself, even default implementation is provided by factory
         CustomerServiceHandler handler = factory.getHandler(country);
         return handler.getCustomer(id,country);

     }
 }
Run Code Online (Sandbox Code Playgroud)

注意:在第二种方法中,我也重复使用接口CustomerServiceHandler(在工厂代码中显示)Reporting and Default implementations.

那么正确的方法是什么,或者如果存在更合适的东西,可以采用什么方法.

问题的第二部分

如果我必须维护两个不同的接口,即一个CustomerServiceHandler用于实现不同国家的实现,一个用于REST层,该怎么办?然后可以是什么设计或替代品.在这种情况下,我认为外观适合.

Mik*_*tta 1

那么什么是正确的方法,或者有什么替代方法

这里你有一个可靠的设计并且充分利用了工厂模式。我提供的是关于这项出色工作的建议,但我认为有很多方法可以增强您所拥有的。

我可以看到该CustomerServiceFacade方法在哪里getCustomer破坏了 SRP。它将检索客户与报告方面结合起来。我同意将报告从该方法中移出会更干净。

那么你的对象将如下所示:

public CustomerServiceFacade{
   public Customer getCustomer(String id, String country){
        return factory.getHandler(country).getCustomer(id,country);
   }
}
Run Code Online (Sandbox Code Playgroud)

那么我们把报告放在哪里呢?

您可以通过单独的界面移动/管理报告。这将允许灵活地实施不同的报告方法并使测试更容易(即模拟报告部分)。

public interface ReportService {
   void report(Customer c, String id, String country);
}
Run Code Online (Sandbox Code Playgroud)

REST 层如何访问报告?

选项 1:REST 通过多个对象访问各种 Customer 功能

的实现ReportService可以通过 .REST 注入到 REST 控制器中CustomerServiceFacade

不确定您使用的 REST 框架是什么,但它可能如下所示:

@GET
@Path("/customer/{Id}/{country}")
public Response getCustomer(@PathParam("Id") String id, @PathParam("country") String country){

    Response r = null;

    // injected implementation of CustomerServiceFacade
    Customer c = customerServiceFacade.getCustomer(id, country);
    if (c!=null){
        // injected implementation of ReportService
        reportService.report(c, id, country);
    }
    else {
        // handle errors ...
    }

    return r;
}
Run Code Online (Sandbox Code Playgroud)

选项 2:REST 通过一个外观/服务访问各种客户功能

您可以允许服务外观层提供向大量提供功能的对象提供简化接口的功能。这可以通过使用多种客户服务方法来完成,这些方法使 REST 层能够通过一个对象访问各种功能,但仍然具有让每种方法更紧密地遵守 SRP 的好处。

在这里,我们注入CustomerServiceFacadeREST 控制器,它调用两个方法 1) 获取客户和 2) 处理报告。外观使用ReportService上面接口的实现。

public CustomerServiceFacade{
   public Customer getCustomer(String id, String country){
        // call the correct CustomerServiceHandler (based on country)
        return factory.getHandler(country).getCustomer(id,country);
   }

   public void report(Customer c, String id, String country){
        // call the reporting service 
        reportService.report(c, id, country);
   }
}
Run Code Online (Sandbox Code Playgroud)

我认为这是 Facade 模式的合理使用,同时在实际方法中仍然具有 SRP。

如果报告实施方式因国家/地区而异,与客户的方式相同,您可以使用其他工厂。

   public void report(Customer c, String id, String country){
        // call the correct reporting service (based on country)
        rptFactory.getInstance(country).report(c,id,country);
   }
Run Code Online (Sandbox Code Playgroud)