2016-08-05 33 views
7

对于一个项目,我们有一个控制器/服务/ DAO架构。我们实行不同提供商的API的调用,所以我们在每一个控制器类结束了与一些像这样的样板代码:设计模式:避免切换决定哪个服务电话

enum { 
    PARTNER_A, PARTNER_B, PARTNER_C 
} 

public class MyController { 
    @Resource PartnerASearchService partnerASearchService; 
    @Resource PartnerBSearchService partnerBSearchService; 
    @Resource PartnerCSearchService partnerCSearchService; 

    public search(InputForm form) { 
     switch(form.getPartnerName()) { 
      case PARTNER_A: partnerASearchService.search(); 
          break; 
      case PARTNER_B: partnerBSearchService.search(); 
          break; 
      case PARTNER_C: partnerCSearchService.search(); 
          break; 
     } 
    } 

    public otherMethod(InputForm form) { 
     switch(form.getProvider()) { 
      case PARTNER_A: partnerAOtherService.otherMethod(); 
          break; 
      case PARTNER_B: partnerBOtherService.otherMethod(); 
          break; 
      case PARTNER_C: partnerCOtherService.otherMethod(); 
          break; 
     } 
    } 
} 

我可以使用哪种设计模式在每个控制器摆脱这个开关?我宁愿代码,像下面的东西:

public class MyController { 
    @Resource ServiceStrategy serviceStrategy; 

    public search(InputForm form){ 
     serviceStrategy.search(form.getPartnerName()) 
     // or 
     serviceStrategy.invoke(SEARCH, form.getPartnerName()) 
    } 

    public otherMethod(InputForm form){ 
     serviceStrategy.other(form.getPartnerName()) 
     // or 
     serviceStrategy.invoke(OTHER, form.getPartnerName()) 
    } 
} 

让serviceStrategy决定哪些服务实现被调用,从而获得具有在一个地方合作伙伴的开关。

我用的术语“策略”,因为我已经被告知这种设计模式可以使,但我不知道的最好的方式来使用它,或者是否有更好的方法来解决这个问题。

编辑:我更新了问题,因为术语提供者是误导。我在输入表单中所列的是我们提出请求的合作伙伴的名称。我要决定哪些是正确执行(几个服务的其中之一)的形式来使用基于合作伙伴的名称

+3

有些话题在这里介绍。 http://stackoverflow.com/questions/126409/ways-to-eliminate-switch-in-code –

+0

我假设这个'PROVIDER_1'是'enum'的一部分? –

+0

是的,不要期望这样的编译,......就是我们所做的,就是这样。 PROVIDER_1,2,3,搜索,其他,...枚举 – luso

回答

0

从不同的答案混合想法,我走到

ServiceProvider.java用于所有的服务供应商超。包含每个合作伙伴

public abstract class ServiceProvider implements IServiceProvider { 
    private final Map<ServiceType, IService> serviceMap; 

    protected ServiceProvider() { 
    this.serviceMap = new HashMap<>(0); 
    } 

    protected void addService(ServiceType serviceType, IService service) { 
    serviceMap.put(serviceType, service); 
    } 

    public IService getService(ServiceType servicetype, PartnerType partnerType) throws ServiceNotImplementedException { 
    try { 
     return this.serviceMap.get(serviceType); 
    } catch (Exception e) { 
     throw new ServiceNotImplementedException("Not implemented"); 
    } 
    } 
} 

ServiceProviderPartnerA.java有每个合作伙伴,它与实际的服务类,针对不同的方法注射服务提供商图的不同服务。

@Service("serviceProviderPartnerA") 
public class ServiceProviderPartnerA extends ServiceProvider { 

    @Resource(name = "partnerASearchService") 
    private ISearchService partnerASearchService; 

    @Resource(name = "partnerABookingService") 
    private IBookingService partnerABookingService; 

    @PostConstruct 
    public void init() { 
    super.addService(ServiceType.SEARCH, partnerASearchService); 
    super.addService(ServiceType.BOOKING, partnerABookingService); 
    } 
} 

ServiceStrategy.java与不同伙伴的服务提供商注射,它实现代码中所需的唯一开关并返回正确的伙伴正确的服务在控制器中使用

@Service("serviceStrategy") 
public class ServiceStrategy implements IServiceStrategy { 

    @Resource(name = "serviceProviderPartnerA") 
    IServiceProvider serviceProviderPartnerA; 

    @Resource(name = "serviceProviderPartnerB") 
    IServiceProvider serviceProviderPartnerB; 

    @Resource(name = "serviceProviderPartnerC") 
    IServiceProvider serviceProviderPartnerC; 

    public IService getService(ServiceType serviceType, PartnerType partnerType) throws PartnerNotImplementedException { 
    switch (partnerType) { 
     case PARTNER_A: 
     return serviceProviderPartnerA.getService(serviceType, partnerType); 
     case PARTNER_B: 
     return serviceProviderPartnerB.getService(serviceType, partnerType); 
     case PARTNER_C: 
     return serviceProviderPartnerC.getService(serviceType, partnerType); 
     default: 
     throw new PartnerNotImplementedException(); 
    } 
    } 
} 

SearchController.java最后,在我的控制器中,我只需要注入serviceStrategy类并使用它来恢复正确的服务。

@Resource(name = "serviceStrategy") 
IServiceStrategy serviceStrategy; 

@RequestMapping(value = "/search", method = RequestMethod.GET, produces = "text/html") 
@ResponseBody 
public String search(@RequestParam(value = "partner", required = true) String partnerType, String... params) { 
    ISearchService service = (ISearchService) serviceStrategy.getService(ServiceType.SEARCH, partnerType); 
    return service.search(params); 
} 

所以,关掉!希望这可以帮助别人

+0

首先,我有一个非常简单的问题,为什么你需要在'ServiceProvider'类中的方法'getService'需要param'partnerType' ,甚至没有使用?这也已经传播到更高层次。 –

+0

是的,对不起,这是一个错字。我仍然很好地调整了这个......实际上,在“getService”中,当我抛出异常时,我添加了partnerType,以便我可以知道哪个合作伙伴服务未实现......否则,您可能无法通过它,如在这个例子中 – luso

+0

否则这个实现似乎工作得很好 – luso

2

一般的模式,形式不应该需要什么“提供者”的任何知识是怎么回事来处理它。相反,提供者应该能够解释他们可以处理哪种输入。

我建议使用责任链的形式(包含重构更换条件与多态性),看起来像这样(的Groovy为简单起见):

interface ProviderService { 
    boolean accepts(InputForm form) 
    void invoke(String foo, InputForm form) 
    void other(InputForm form) 
} 

ProviderService每个实现实现accepts表明它是否可以处理特定的表单,并且您的控制器存储List<ProviderService> services而不是单个引用。然后,当你需要处理一个表单,您可以使用:

ProviderService service = services.find { it.accepts(form) } 
// error handling if no service found 
service.other(form) 

这种模式是在一个大框架中使用的一个完整的例子见the Spring conversion service

0

事实上,您可以在这里使用Strategy模式。它看起来像这样。

在这里,您必须从InputForm获得指定的ServiceProvider

你可以有StrategyMaker类这样的事情。

Public class StrategyMaker{ 

    public SeriveProvider getProviderStrategy(InputForm inputForm){ 
     return inputForm.getProvider(); 
    } 
} 

而内部控制器,你可以做这样的事情。

public class MyController{ 
    StrategyMaker strategyMaker; 

    public search(InputForm form){ 
     strategyMaker.getProviderStategy(form).search(); 
    } 
} 

这将是一个理想的解决方案,如果您知道所有提供者战略正手列表。策略模式在列表不断增长时无法保持开放关闭。

还有一件事是当你引用一个模式时,总是试图获得大局。不要拘泥于任何源提供的实现示例。永远记住它是执行,而不是执行。

+1

下来的选民可以列出这个设计中的缺陷,让其他用户得到更好的图片吗? – JavaHopper

+0

@JavaHopper我也相信投票应该有一个描述,至少,我可以知道他有什么不对,这样我们可以在任何时候争论,或者可以解决任何误解。我无法理解我在这里做错了什么。 :( –

+1

Inputform与请求中的数据有关,它不应该有对ServiceProvider对象的引用。为什么需要将StrategyMaker类放在它们之间,而不是直接调用inputForm.getProvider?如果您的StrategyMaker实现只是一个“存根”的例子,请在答案中提及。 – jhyot

0

首先通过将提取程序查找代码提取到自己的方法来删除重复提供程序查找代码。

public class MyController { 

    @Resource 
    ProviderService searchServiceProvider1; 
    @Resource 
    ProviderService searchServiceProvider2; 
    @Resource 
    ProviderService searchServiceProvider3; 

    public void search(InputForm form) { 
     String provider = form.getProvider(); 
     ProviderService providerService = lookupServiceProvider(provider); 
     providerService.search(); 
    } 

    public void other(InputForm form) { 
     String provider = form.getProvider(); 
     ProviderService providerService = lookupServiceProvider(provider); 
     providerService.other(); 
    } 

    private ProviderService lookupServiceProvider(String provider) { 
     ProviderService targetService; 
     switch (provider) { 
     case PROVIDER_1: 
      targetService = searchServiceProvider1; 
      break; 
     case PROVIDER_2: 
      targetService = searchServiceProvider2; 
      break; 
     case PROVIDER_3: 
      targetService = searchServiceProvider3; 
      break; 
     default: 
      throw new IllegalStateException("No Such Service Provider"); 
     } 
     return targetService; 
    } 

} 

至少可以提高lookupServiceProvider方法和使用的地图,以避免开关。

private Map<String, ProviderService> providerLookupTable; 

private Map<String, ProviderService> getProviderLookupTable(){ 
    if(providerLookupTable == null){ 
     providerLookupTable = new HashMap<String, ProviderService>(); 
     providerLookupTable.put(PROVIDER_1, searchServiceProvider1); 
     providerLookupTable.put(PROVIDER_2, searchServiceProvider2); 
     providerLookupTable.put(PROVIDER_3, searchServiceProvider3); 

    } 
    return providerLookupTable; 
} 

private ProviderService lookupServiceProvider(String provider) { 
    Map<String, ProviderService> providerLookupTable = getProviderLookupTable(); 
    ProviderService targetService = providerLookupTable.get(provider); 
    if(targetService == null){ 
     throw new IllegalStateException("No Such Service Provider"); 
    } 

    return targetService; 
} 

最后,你会认识到,你可以引入一个ProviderServiceLocator,将查找逻辑这个类,并让MyController使用ProvicerServiceLocator

关于服务提供商接口和标准java服务提供商查询的详细说明和示例代码可以在我的博客A plug-in architecture implemented with java中找到。当合作伙伴不经常改变

+0

其实这是一个错字。控制器中的第二种方法不调用searchServices,而是调用其他ServiceProvideX。我已经更新了我的问题 – luso

0

一个可能的解决方案:

class ServiceFactory { 

    @Resource PartnerService partnerASearchService; 
    @Resource PartnerService partnerBSearchService; 
    @Resource PartnerService partnerCSearchService; 

    public static PartnerService getService(String partnerName){ 
    switch(partnerName) { 
     case PARTNER_A: return partnerASearchService; 
     case PARTNER_B: return partnerBSearchService; 
     case PARTNER_C: return partnerCSearchService; 
    } 
} 



public class MyController { 
    @Resource ServiceFactory serviceFactory; 

    public search(InputForm form) { 
     serviceFactory.getService(form.getProvider()).search() 
    } 

    public otherMethod(InputForm form) { 
     serviceFactory.getService(form.getProvider()).otherMethod() 
    } 
} 
+0

不同的服务提供了不同的方法,...这不会起作用 – luso