membersound membersound - 5 months ago 9
Java Question

How to delegate to services by class type?

I have different class types, and depending on some conditions, I want to delegate to the appropriate

service
that can handle those class types.

Example:
I have several classes as follows.

class Student;
class Prof;
...


For each class there is a service, implementing:

interface IPersonService {
void run();
}


And I have a
mode
that is found by some conditions:

enum PersonType {
STUDENT, PROF;
}


When I delegate:

@Autowired
private StudentService studentService;

@Autowired
private ProfService profService;

//@param mode assume known
public void delegate(PersonType mode) {

//assume there are several of those switch statements in my business code
switch (mode) {
case STUDENT: studentService.run(); break;
case PROF: profService.run(); break;
default: break;
}
}


Problem: When introducing additional classes, I have to both modify the
PersonType
and add an additional enum (which is no problem), but I also have to extend any
switch
statement and add calls to additional delegation services. Also I have to explicit autowire those services to the switch delegator.

Question: how could I optimize this code, to just implementing new Services for any additional class, and not having to touch any of the switch statements?

Answer

Add a method to IPersonService so that the implementation of the method can tell the program what type of persons it handles:

interface IPersonService {
    PersonType supportedPersonType();
    void run();
}

In the service that does the delegation, inject a List<IPersonService>, which Spring will fill with all the implementations of IPersonService that it can find. Then implement the delegate method to look through the list to find the first IPersonService that can handle the specific type.

@Autowired
private List<IPersonService> personServices;

public void delegate(PersonType mode) {
    for (IPersonService personService : personServices) {
        if (personService.supportedPersonType().equals(mode)) {
            personService.run();
            break;
        }
    }
}

This way, you can add new implementations of IPersonService without having to change the service that does the delegation.

To avoid having to go through the loop each time delegate is called, you could build a Map beforehand so that the right IPersonService can be looked up quickly:

class DelegatingService {
    @Autowired
    private List<IPersonService> personServices;

    private Map<PersonType, IPersonService> personServiceMap;

    @PostConstruct
    public void init() {
        personServiceMap = new HashMap<>();
        for (IPersonService personService : personServices) {
            personServiceMap.put(personService.supportedPersonType(), personService);
        }
    }

    public void delegate(PersonType mode) {
        personServiceMap.get(mode).run();
    }
}

(Error handling omitted for simplicity).

Comments