Как заменить инструкцию switch на IOC, чтобы я мог поддерживать принцип SOLID - PullRequest
7 голосов
/ 21 февраля 2010

Я хотел избежать оператора switch. У меня более 30 типов документов. Существует также возможность, что мне нужно будет добавить больше типов документов в будущем. Я предпочел бы передать IDocument и иметь тип, указанный в реализации IDocument. Что-то еще, что я забыл упомянуть, было ProgressNoteViewModel, LabViewModel ... все они наследуются от WorkspaceViewModel, и все конструкторы конкретной реализации принимают тип IPatient в качестве параметра. Я также использую Castle в качестве контейнера IoC

Я хотел бы изменить код на что-то вроде

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

У меня есть следующий код:

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

Ответы [ 2 ]

4 голосов
/ 21 февраля 2010

Полностью не проверено:

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

Теперь ваш телефонный код:

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 правки с первого поста; продолжайте видеть ошибки]

[Далее редактировать, отмечая, что вы используете Castle IOC]

В свою конфигурацию Castle xml вы можете добавить (и я работаю над лишь смутным знанием Castle здесь)

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

Тогда вам не нужен ViewModelBuilderFactory, вы можете просто заменить

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

с

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

Теперь вам вообще не нужен оператор switch.

Однако, стоит отметить, что переключатели не являются злыми, они просто плохо пахнут и, как все неприятные запахи, должны быть изолированы от всего, что пахнет хорошо; это то, для чего предназначен шаблон фабрики.

1 голос
/ 30 мая 2012

Вместо контейнера IoC я бы попытался объединить шаблоны стратегии и фабрики. Если вам нужны настраиваемые параметры конструктора для каждого случая, я бы предположил, что вам нужен правильный контейнер IoC с разводкой.

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

Ссылаясь на ответ выше с использованием Castle Windsor, следующий код может сделать то же самое, используя именованные компоненты, но инициализированный в коде:

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

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