门面或装饰

时间:2015-02-26 02:27:35

标签: java design-patterns decorator facade

背景:

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

首先根据我使用的工厂模式来解决不同的实现,如下所示:

所有基于国家/地区的实施的通用界面

public Interface CustomerServiceHandler{
   Cusomer getCustomer(String id, String country);
}

然后出厂

public class CustomerServiceHandlerFactory{
   public CustomerServiceHandler getHandler(String country){...};
}

使用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;
   }
}

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

使用装饰器模式实现:

//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);

     }
 }

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

那么正确的方法是什么,或者如果存在更合适的东西,还有什么替代方法。

问题的第二部分

如果我必须维护两个不同的接口,即一个CustomerServiceHandler来实现不同的国家'实现和一个服务REST层。然后可以是什么设计或替代品。在这种情况下,我认为外观适合。

1 个答案:

答案 0 :(得分:1)

  

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

的替代方法是什么

这里有一个坚实的设计,并且很好地利用了工厂模式。我提供的是关于这项优秀工作的建议,但我认为有很多方法可以增强你的成就。

我可以看到CustomerServiceFacade方法getCustomer打破SRP的位置。它结合了检索客户和报告方面。我同意将报告移出该方法会更清晰。

然后你的对象看起来像这样:

public CustomerServiceFacade{
   public Customer getCustomer(String id, String country){
        return factory.getHandler(country).getCustomer(id,country);
   }
}

那么我们将报告放在哪里?

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

public interface ReportService {
   void report(Customer c, String id, String country);
}

REST层如何访问报告?

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

可以使用ReportServiceCustomerServiceFacade的实现注入到REST控制器中。

不确定您使用的是什么框架用于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;
}

选项2:REST通过一个Facade / Service

访问各种客户功能

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

这里我们将CustomerServiceFacade注入REST控制器,并调用两个方法1)以获取客户和2)处理报告。 Facade使用上面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);
   }
}

我认为这是合理使用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);
   }