7

Ich wollte die switch-Anweisung vermeiden. Ich habe über 30 Dokumenttypen. Es besteht auch die Möglichkeit, dass ich weitere Dokumenttypen hinzufügen muss. Ich würde lieber IDocument übergeben und den in der Implementierung von IDocument angegebenen Typ haben. Etwas anderes, das ich vergessen habe zu erwähnen, war ProgressNoteViewModel, LabViewModel ... alle erben von WorkspaceViewModel und alle konkreten Implementierungskonstruktoren nehmen einen Typ IPatient als Parameter. Ich bin auch mit Schloss als mein IoC ContainerWie ersetze ich eine Schaltanweisung mit IOC, damit ich das SOLID-Prinzip beibehalten kann

Ich möchte den Code so etwas wie

viewModel = new TreeViewModel(repository.GetPatientDocumentListing(IDocumentType); 
this.DocTreeViewModel = viewModel; 
//How would I then be able to instantiate the right ViewModel 
//based on IDocumentType and also pass a object into the 
//constructor that is not know at compile time 

ich den folgenden Code haben Refactoring:

switch (docType) 
{ 
    case "ProgressNotes": 
     viewModel = new TreeViewModel(repository.GetPatientProgressNotes()); 
     this.DocTreeViewModel = viewModel; 
     ProgressNoteViewModel workspace = ProgressNoteViewModel.NewProgressNoteViewModel(_patient); 
     break; 
    case "Labs": 
     viewModel = new TreeViewModel(repository.GetPatientLabs()); 
     this.DocTreeViewModel = viewModel; 
     LabViewModel workspace = LabViewModel.NewLabViewModel(_patient); 
     break; 
} 
this.Workspaces.Add(workspace); 
this.SetActiveWorkspace(workspace); 
+0

Welche IoC-Container verwenden Sie? – smaclell

Antwort

4

Völlig ungetestet:

public class ViewModelBuilderFactory 
{ 
    public IViewModelBuilder GetViewModelBuilder (string docType, IRepository repository) 
    { 
     switch (docType) 
     { 
      case "ProgressNotes": 
       return new ProgressNotesViewModelBuilder(repository); 
      case "Labs": 
       return new LabsViewModelBuilder(repository); 
      default: 
       throw new ArgumentException(
        string.Format("docType \"{0}\" Invalid", docType); 
     } 
    } 
} 

public interface IViewModelBuilder 
{ 
    TreeViewModel GetDocTreeViewModel(); 
    WorkSpace GetWorkSpace(Patient patient); 
} 

public class LabsViewModelBuilder : IViewModelBuilder 
{ 
    private IRepository _repository; 
    public LabsViewModelBuilder(IRepository repository) 
    { 
     _repository = repository; 
    } 

    public TreeViewModel GetDocTreeViewModel() 
    { 
     return new TreeViewModel(_repository.GetPatientLabs()); 
    } 

    public Workspace GetWorkspace(Patient patient) 
    { 
     return LabViewModel.NewLabViewModel(patient); 
    } 
} 

public class ProgressNotesViewModelBuilder : IViewModelBuilder 
{ 
    private IRepository _repository; 
    public ProgressNotesViewModelBuilder(IRepository repository) 
    { 
     _repository = repository; 
    } 

    public TreeViewModel GetDocTreeViewModel() 
    { 
     return new TreeViewModel(_repository.GetPatientProgressNotes()); 
    } 

    public Workspace GetWorkspace(Patient patient) 
    { 
     return ProgressNoteViewModel.NewProgressNoteViewModel(patient); 
    } 
} 

Jetzt lautet Ihr Anrufcode:

ViewModelBuilderFactory factory = new ViewModelBuilderFactory(); 
IViewModelBuilder modelBuilder = factory.GetViewModelBuilder(docType, repository); 
this.DocTreeViewModel = modelBuilder.GetDocTreeViewModel(); 
Workspace workspace = modelBuilder.GetWorkspace(patient); 
this.Workspaces.Add(workspace); 
this.SetActiveWorkspace(workspace); 

[4 Änderungen seit dem ersten Beitrag; halten sehen Fehler]

[Weitere bearbeiten Hinweis darauf, dass Sie verwenden Castle IOC]

In Ihrem Schloss XML-Konfiguration, könnten Sie hinzufügen (und ich arbeite nur eine vage Kenntnis von Schloss hier)

<component id="ProgressNotesViewModelBuilder" 
      type="MyNamespace.ProgressNotesViewModelBuilder, MyAssembly"> 
    <parameters> 
     <!-- reference to repository here --> 
    </parameters> 
</component> 
<component id="LabsViewModelBuilder" 
      type="MyNamespace.LabsViewModelBuilder, MyAssembly"> 
    <parameters> 
     <!-- reference to repository here --> 
    </parameters> 
</component> 

Dann brauchen Sie nicht die ViewModelBuilderFactory, können Sie einfach

ersetzen
IViewModelBuilder modelBuilder = factory.GetViewModelBuilder(docType, repository); 

mit

IViewModelBuilder modelBuilder = (IViewModelBuilder) 
    container.Resolve(docType + "ViewModelBuilder"); 

Jetzt brauchen Sie Ihre Switch-Anweisung überhaupt nicht mehr.

Es ist jedoch erwähnenswert, dass Schalter nicht böse sind, sie nur schlecht riechen und wie alle schlechten Gerüche von allem isoliert werden sollten, das gut riecht; Das soll das Factory-Muster erreichen.

+0

Yarg nur wenige Minuten geschlagen. Sie können auch jede ModelBuilder-Klasse die IViewModelBuilder-Schnittstelle implementieren. Wenn Sie wirklich einen IoC-Container benötigen, können Sie den docType verwenden, um benannte Konfigurationen zu unterscheiden, aber möglicherweise ist zu viel Geschäftslogik im Container konfiguriert. – smaclell

+0

Bedeutet, dass sie die Schnittstelle implementieren, macht es sonst keinen Sinn. Bearbeitet. – pdr

+0

Die Änderung, die in das Inversion-of-Control-Prinzip passt, hängt vom 'IViewModelBuilder' ab und nicht von den konkreten Implementierungen' ProgressNotesViewModelBuilder' oder 'LabsViewModelBuilder'. – smaclell

1

Anstelle von IoC-Container würde ich versuchen, Strategie und Fabrikmuster zu kombinieren. Wenn Sie für jeden Fall benutzerdefinierte Konstruktorparameter benötigen, würde ich annehmen, dass Sie einen richtigen IoC-Container mit Verkabelung benötigen.

class ViewModelBuilderFactory 
{ 
    private Dictionary<string, System.Type> resolver; 

    public void ViewModelBuilderFactory() 
    { 
     resolver = new Dictionary<string, Type> 
     { 
      {"ProgressNotes", typeof(ProgressNotesViewModelBuilder)}, 
      {"Labs", typeof(LabsViewModelBuilder)} 
     }; 
    } 

    public IViewModelBuilder GetViewModelBuilder(string key) 
    { 
     System.Type type = this.resolver[key]; 
     return (IViewModelBuilder)Activator.CreateInstance(type); 
    } 

} 

EDIT

oben mit Schloss Windsor auf die Antwort Bezug genommen, könnte der folgende Code das gleiche tun genannten Komponenten verwenden, aber in Code initialisiert:

container.Register(Component 
.For<IViewModelBuilder>() 
.ImplementedBy<ProgressNotesViewModelBuilder>() 
.Named("ProgressNotes")); 
container.Register(Component 
.For<IViewModelBuilder>() 
.ImplementedBy<LabsViewModelBuilder>() 
.Named("Labs")); 

var builder = container.Resolve<IViewModelBuilder>(key); 
Verwandte Themen