7

我想避免 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);
4

2 回答 2

4

完全未经测试:

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 语句。

然而,值得注意的是,开关并不邪恶,它们只是闻起来很臭,就像所有难闻的气味都应该与所有闻起来很香的东西隔离开一样;这就是工厂模式旨在实现的目标。

于 2010-02-21T00:10:19.093 回答
1

我会尝试将策略和工厂模式结合起来,而不是 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);
    }

}

编辑

参考上面使用 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);
于 2012-05-30T12:54:10.273 回答