Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I replace a switch statement with IOC so I can maintain SOLID principle

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);
like image 713
William Avatar asked Feb 20 '10 23:02

William


People also ask

Can switch statement replace if else?

A switch statement can replace multiple if checks. It gives a more descriptive way to compare a value with multiple variants.

Why are switch statements useful?

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.

Do switch statements go in order?

The switch expressions are evaluated in textual order. Execution transfers to the first switch label that matches the switch expression.

Can switch statement have case 0?

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.


2 Answers

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);
like image 34
Alexey Zimarev Avatar answered Sep 30 '22 07:09

Alexey Zimarev


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.

like image 197
pdr Avatar answered Sep 30 '22 07:09

pdr