2016-06-21 4 views
6

Ich habe verschiedene Klassenarten, und abhängig von einigen Bedingungen möchte ich an die entsprechenden service delegieren, die diese Klassenarten behandeln können. Beispiel: Ich habe mehrere Klassen wie folgt.Wie an Dienste nach Klassenart delegieren?

class Student; 
class Prof; 
... 

Für jede Klasse gibt es einen Service, Implementierung:

interface IPersonService { 
    void run(); 
} 

Und ich habe eine mode, die von einigen Bedingungen zu finden ist:

enum PersonType { 
    STUDENT, PROF; 
} 

Wenn ich delegieren:

@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: Wenn ich zusätzliche Klassen einzuführen, muss ich sowohl die PersonType ändern und eine zusätzliche enum hinzufügen (was kein Problem ist), aber ich muss auch alle switch Anweisung zu erweitern und Anrufe zu zusätzlichen Delegationsdienste hinzufügen. Außerdem muss ich diese Dienste explizit dem Switch Delegator zuweisen.

Frage: Wie könnte ich diesen Code optimieren, nur neue Services für jede zusätzliche Klasse implementieren und keine der Switch-Anweisungen berühren müssen?

+0

Sie eine Zuordnung in Enum halten sich person mit dem Service und in Delegierter zu assoziieren nur den Dienst auf person erhalten basierend & es nennen. – Sanjeev

+0

Aber ich kann die Dienste nicht in die Enum injizieren, kann ich? – membersound

+0

Die Ursache des Problems liegt darin, dass die Servicelogik von den Daten getrennt ist, die sie bedient. Die Trennung von Daten und Logik heißt [Procedural Programming] (https://en.wikipedia.org/wiki/Procedural_programming). In [Objektorientierte Programmierung] (https://en.wikipedia.org/wiki/Objektorientierte_Programmierung) wird die Servicelogik mit den von ihr bereitgestellten Daten kombiniert, so dass keine Delegierung erforderlich ist. – jaco0646

Antwort

5

ein Verfahren zu IPersonService hinzufügen, so dass die Durchführung des Verfahrens, das Programm kann sagen, welche Art von Personen, die sie behandelt:

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

Im Dienst, der die Delegation der Fall ist, injizieren ein List<IPersonService>, die Frühling füllen mit allen Implementierungen von IPersonService, die es finden kann. Dann implementieren Sie die delegate Methode, um durch die Liste zu suchen, um die erste IPersonService zu finden, die den spezifischen Typ verarbeiten kann.

@Autowired 
private List<IPersonService> personServices; 

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

Auf diese Weise können Sie neue Implementierungen von IPersonService hinzufügen, ohne den Dienst ändern zu müssen, der die Delegation der Fall ist.

Um zu vermeiden, durch die Schleife gehen jedes Mal delegate genannt wird, könnte man einen Map vorher bauen, so dass die richtige IPersonService schnell nachgeschlagen werden können:

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

(Fehler der Einfachheit halber weggelassen Behandlung).

+0

Das ist eine sehr schöne und wahrscheinlich die richtige Lösung für mein Problem! – membersound

0

Sie können den Service-Bean-Namen (oder -Klassentyp) in der Enumeration speichern und die Beans mithilfe von getBean nach Name (oder nach Klassentyp) aus dem Anwendungskontext abrufen.

Außerdem müssen alle Dienste eine Schnittstelle implementieren, die über die Run-Methode verfügt.

interface ModeService { 
    void run(); 
    } 

    enum PersonType { 
     STUDENT("studentService"), PROF("profService"); 
     private String serviceBean; 
     public PersonType(String serviceBean) { 
       this.serviceBean = serviceBean); 
     } 
     public String getServiceBean() { 
       return serviceBean; 
     } 
    } 

in delegate kann dann Folgendes verwendet werden.((ModeService)applicationContext.getBean(mode.getServiceBean()).run()

Auf diese Weise muss nur die Enumeration mit dem zu verwendenden Diensttyp aktualisiert werden, und es ist keine Änderung der Delegate-Methode erforderlich.

1

In meiner Anwendung lösten wir ähnliche Probleme, indem wir Dienste in eine Karte einfügten. Berücksichtigen Sie Map<PersonType,IPersonService> serviceMap definiert als Bean und in Ihre Klasse injiziert. delegieren Dann einfache Methode tun

public void delegate(PersonType mode) { 
    IPersonService service = serviceMap.get(mode); 
    if (service!=null){ 
     service.run(); 
    }else{ 
     //do something if service is null 
    } 
}