Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Design pattern: avoid switch to decide which service call

For a project, we have a Controller/Service/DAO architecture. We implement calls to different providers' APIs so we ended up with some boilerplate code like this in every controller class:

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

Which design pattern can I use to get rid of this switch in every controller? I would prefer the code to be something like the below:

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())
    }
}

letting the serviceStrategy decide which service implementation to be called, and thus having the partner's switch in a single place.

I've used the term "strategy" because I've been told this design pattern could make it, but I'm not sure of the best way to use it or if there is a better approach to solve this problem.

EDIT: I've updated the question as the term provider is misleading. What I have in the input form is the name of the partner for which we do the request. I want a pattern that decides which is the correct implementation (which one of the several services) to use based on the partner's name in the form

like image 907
luso Avatar asked Aug 05 '16 22:08

luso


People also ask

Which of the design pattern says that avoid coupling?

Chain of responsibility pattern is used to achieve loose coupling in software design where a request from the client is passed to a chain of objects to process them.

What we can use instead of switch case?

In the release of the 8th version of PHP, the match() function is introduced which is the new alternative of switch-case. It is a powerful feature that is often the best decision to use instead of a switch-case.

When should design patterns not be used?

If a problem has two solutions, one that fits in ten lines of code, and another one with hundreds of lines of code along with a pattern, please consider not using the pattern. Their presence isn't a quality measurement.

What is the best approach in design patterns?

One of the most popular design patterns used by software developers is a factory method. It is a creational pattern that helps create an object without the user getting exposed to creational logic. The only problem with a factory method is it relies on the concrete component.


1 Answers

Generally, the form shouldn't need any knowledge of what "provider" is going to handle it. Instead, the providers should be able to explain which kinds of inputs they can handle.

I recommend using a form of Chain of Responsibility (incorporating the refactoring Replace Conditional with Polymorphism) that looks something like this (Groovy for simplicity):

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

Each implementation of ProviderService implements accepts to indicate whether it can handle a particular form, and your controller stores a List<ProviderService> services instead of individual references. Then, when you need to process a form, you can use:

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

See the Spring conversion service for a comprehensive example of this pattern that's used in a major framework.

like image 63
chrylis -cautiouslyoptimistic- Avatar answered Nov 15 '22 00:11

chrylis -cautiouslyoptimistic-