2016-07-29 91 views
0

Es gibt eine Klassenhierarchie von Aufgaben, die von der Klasse A durchgeführt werden könnenlange case-Anweisung auf Argument Klasse Vermeidung

Innerhalb der Klasse A gibt es eine Erklärung für die Task-Klasse und den Aufruf der entsprechenden Handler Überprüfung langen Falles ist Methode.

Class Task 
End Class 

Class TaskX 
Inherits Task 
End Class 

Class TaskY 
Inherits Task 
End Class 

... 


Class A 
    Public Sub PerformTask(Task As Task) 
     Select Case Task.GetType 
      Case GetType(TaskX) 
       PerformTaskX(CType(Task, TaskX)) 
      Case GetType(TaskY) 
       PerformTaskY(CType(Task, TaskY)) 
     End Select 
    End Sub 


    Private Sub PerformTaskX(Task As TaskX) 
    End Sub 

    Private Sub PerformTaskY(Task As TaskY) 
    End Sub 

    ... 
End Class 

Die Aufgaben haben ihre individuellen Parameter, Validierungslogik usw. Das Beispiel ist vereinfacht.

Ich möchte die Fallaussage vermeiden und suche nach alternativen Lösungen.

Die allgemeine Idee ist die Zuordnung eines Klassentyp zu einem Handler (der Klasse), die irgendwie wie dies funktionieren könnte:

Delegate Sub Handler(Task As Task) 

Private TypeToHandlerDict As New Dictionary(Of System.Type, Handler) From { _ 
      {GetType(TaskX), AddressOf PerformTaskX}, _ 
      {GetType(TaskY), AddressOf PerformTaskY} _ 
     } 

so, dass ich die PerformTask Methode Inhalt mit dem viel eleganter ersetzen:

Public Sub PerformTask(Task As Task) 
    Dim handler As Handler = Nothing 
    If Not TypeToHandlerDict.TryGetValue(Task.GetType, handler) Then Exit Sub 
    handler(Task) 
End Sub 

Aber es funktioniert nicht.

Der Delegat bindet an Typ Task, aber die Handler-Methoden akzeptieren einen Task-Unterklasse-Parameter. Ich verwende Option Strict. Ich könnte alle Handler-Methoden neu schreiben, um ein Task-Objekt zu akzeptieren und eine explizite Umwandlung durchzuführen, aber das ist ebenso unelegant.

Irgendwelche Vorschläge? Vielen Dank!

Update: Hier ist die Lösung dank der Antwort von @Sehnsucht.

Class Task 
    Public MustOverride Sub Perform(a As A) 
End Class 

Class TaskX 
Inherits Task 
    Public Overrides Sub Perform(a As A) 
     a.DoThis() 
    End Sub 
End Class 

Class TaskY 
Inherits Task 
    Public Overrides Sub Perform(a As A) 
     a.DoThat() 
    End Sub 
End Class 

... 


Class A 
    Public Sub PerformTask(Task As Task) 
     Task.Perform(Me) 
    End Sub 


    Public Sub DoThis() 
    End Sub 

    Public Sub DoThat() 
    End Sub 

    ... 
End Class 
+0

Können Sie eine 'Perform' oder' Execute' Methode, um die Aufgabe Klassen hinzufügen und lassen sie die Arbeit machen? Vielleicht mit einer 'ITask'-Schnittstelle, falls nötig. – Plutonix

+0

Können Sie PerformTask als Muss-Funktion innerhalb der Task überschreiben und jede TaskX ihre eigene Version implementieren? Auf diese Weise rufen Sie einfach task.PerformTask() –

+1

Nicht umsonst, aber Sie haben 8 Fragen gestellt und 10 Antworten bekommen, aber keine von ihnen als akzeptiert markiert. Die Annahme der besten Antwort, falls vorhanden, und Upvoting hilft anderen Benutzern, gute Antworten zu finden. Die [Tour] erklärt dies und andere Dinge in sehr kurzer Form. – Plutonix

Antwort

0

Alle PerformTask... haben die gleiche Signatur und dass von den Delegierten erzwungen ist Handler Sie in Ihrem Beitrag zeigte.
Also sollten Sie eine Overridable-Methode in Ihrer Task Klasse mit einer Standardimplementierung machen (oder markieren Sie die Methode als MustOverride, wenn die Task-Klasse MustInherit ist), Methode, die von ihren Erben überschrieben wird, um den Job entsprechend ihrem speziellen Fall zu tun.

Class Task 
    Public Overridable Sub Perform() 
     ' implementation specific to Task ("default" case) 
    End Sub 
End Class 

Class TaskX 
    Inherits Task 

    Public Overrides Sub Perform() 
     ' implementation specific to TaskX 
     ' you can call the parent implementation if needed with 
     MyBase.Perform() 
    End Sub 
End Class 

'Usage 
Class A 
    Public Sub PerformTask(task As Task) 
     task.Perform() ' will call the "right one" according to it's runtime type 
    End Sub 
End Class 
+0

Das Ausführen einer Task beinhaltet eine Geschäftslogik innerhalb der Klasse A ("_wie soll es gemacht werden"), während eine Task nur weiß, "was getan werden sollte". Erstellen einer Execute-Methode mit einem Klasse-A-Parameter in der Aufgabe und lassen Sie die Task offengelegte Business-Logik der Klasse A aufrufen, obwohl ich diese Lösung nicht mag. – Manolis

+0

@Manolis wurde nie gesagt, dass Tasks-Klassen nur Daten über das * was * und nicht das * wie * enthielten. Fast das Gegenteil, für mich, als Sie sagten, dass jede Aufgabe individuelle Parameter, Validierungslogik ... hat.Es bedeutete für mich, dass jeder wusste, wie es funktionieren würde. Mit diesen zusätzlichen Infos hätte Ihr Kommentar neben dem ursprünglichen Beitrag vielleicht auch anders geantwortet :-) – Sehnsucht

+0

Anscheinend ist die einzige Lösung, die funktioniert, in der Tat, den Polymorphismus zu nutzen und jeden Task-Nachkommen in die Lage zu versetzen, sich selbst zu erfüllen. Um dies in unserem System zu implementieren, muss ich einige Logik der Klasse A verfügbar machen und die polymorphe Perform-Methode von Task ändern, um A als Parameter zu akzeptieren, so dass es die gewünschte Logik aufrufen kann. Vielen Dank! – Manolis