我想避免使用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);
答案 0 :(得分: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语句。
然而,值得注意的是开关不是邪恶的,它们只是闻起来很糟糕,并且所有难闻的气味都应该与闻起来好的东西隔离开来;这就是工厂模式的目标。
答案 1 :(得分: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);