How to replace switch statement with IOC so that I can support SOLID principle

I wanted to avoid the switch statement. I have more than 30 types of documents. It is also possible to add more types of documents moving forward. I would rather pass in an IDocument and have the type specified in the implementation of IDocument. Another thing I forgot to mention was ProgressNoteViewModel, LabViewModel ... everyone inherits from WorkspaceViewModel, and all concrete implementation constructors accept an IPatient type parameter as a parameter. I also use Castle as my IoC container

I would like to reorganize the code to something like

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 

I have the following code:

 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); 
+7
c # switch-statement inversion-of-control solid-principles
source share
2 answers

Completely untested:

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

Now your calling code:

 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 edits from the first post; continue to see errors]

[Next Edit, noting that you are using Castle IOC]

In the configuration of your xml code you can add (and I only work with vague lock knowledge here)

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

Then you do not need ViewModelBuilderFactory, you can just replace

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

from

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

Now you do not need a switch statement.

However, it is worth noting that the switches are not evil, they just smell bad and, like all unpleasant odors, should be isolated from everything that smells good; this is what the Factory pattern aims for.

+4
source share

Instead of an IoC container, I would try to combine strategy and factory templates. If you need custom constructor options for each case, I would suggest that you need the right wired IoC container.

 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

Referring to the answer above using Castle Windsor, the following code could do the same using named components, but was initialized in code:

 container.Register(Component .For<IViewModelBuilder>() .ImplementedBy<ProgressNotesViewModelBuilder>() .Named("ProgressNotes")); container.Register(Component .For<IViewModelBuilder>() .ImplementedBy<LabsViewModelBuilder>() .Named("Labs")); var builder = container.Resolve<IViewModelBuilder>(key); 
+1
source share

All Articles