对于项目,我们有一个Controller / Service / 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决定调用哪个服务实现,从而将合作伙伴的切换放在一个地方。
我使用了“策略”一词,因为我被告知这种设计模式可以实现,但我不确定使用它的最佳方式,或者是否有更好的方法来解决这个问题。
编辑:我已经更新了这个问题,因为术语提供商会产生误导。我在输入表单中的内容是我们执行请求的合作伙伴的名称。我想要一个模式,根据表单中的合作伙伴名称决定哪个是正确的实现(几个服务中的哪一个)
答案 0 :(得分: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。
答案 1 :(得分:0)
确实,您可以在此处使用Strategy模式。它看起来像这样。
在这里,您必须从 InputForm 获取指定的 ServiceProvider 。
你可以拥有策略大师类这样的东西。
Public class StrategyMaker{
public SeriveProvider getProviderStrategy(InputForm inputForm){
return inputForm.getProvider();
}
}
在内部控制器中你可以做这样的事情。
public class MyController{
StrategyMaker strategyMaker;
public search(InputForm form){
strategyMaker.getProviderStategy(form).search();
}
}
如果您事先了解所有提供商策略的列表,这将是一个理想的解决方案。当列表不断增长时,策略模式无法保持Open-Close-Princple。
另外一件事是,当你提到一个模式时,总是试图全面了解。不要严格查看任何源提供的实现示例。永远记住它是 实施,不是实施。
答案 2 :(得分: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。
答案 3 :(得分: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 注册了不同的合作伙伴&#39;服务提供商,它实现代码中唯一需要的交换机,并为控制器中使用的正确伙伴返回正确的服务
@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);
}
所以,关掉!希望这有助于某人