I wanted to avoid the switch statement. I have over 30 document types. There is also a possibility I will need to add more document types moving forward. I would rather pass IDocument and have the type specified in the implementation of IDocument. Something else I forgot to mention was ProgressNoteViewModel, LabViewModel ... all inherit from WorkspaceViewModel and all of the concrete implementation constructors take a type IPatient as parameter. I am also using Castle as my IoC container
I would want to refactor 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);
A switch statement can replace multiple if checks. It gives a more descriptive way to compare a value with multiple variants.
The main reasons for using a switch include improving clarity, by reducing otherwise repetitive coding, and (if the heuristics permit) also offering the potential for faster execution through easier compiler optimization in many cases.
The switch expressions are evaluated in textual order. Execution transfers to the first switch label that matches the switch expression.
The switch statement can include any number of case instances. However, no two constant-expression values within the same switch statement can have the same value.
Instead of IoC container I would try to combine strategy and factory patterns. If you need custom constructor parameters for each case, then I would assume you need a proper IoC container with wiring.
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 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);
Entirely 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 is:
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 since first post; keep seeing mistakes]
[Further Edit noting that you're using Castle IOC]
In your Castle xml configuration, you could add (and I'm working on only a vague knowledge of Castle 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 don't need the ViewModelBuilderFactory, you can just replace
IViewModelBuilder modelBuilder = factory.GetViewModelBuilder(docType, repository);
with
IViewModelBuilder modelBuilder = (IViewModelBuilder)
container.Resolve(docType + "ViewModelBuilder");
Now you don't need your switch statement at all.
However, it's worth noting that switches aren't evil, they just smell bad and like all bad smells should be isolated from everything that smells good; that's what the Factory pattern is intended to achieve.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With