设计领域模型的时候,属性的set
块会带来一些问题,例如下面的一个类。
public class SearcherBase
{
public IApplication Application { get; set; }
public virtual IIndexManager IndexManager { get; set; }
public virtual async Task<SearchResult> SearchAsync(Query query)
{ //some code }
}
当客户端调用该类时,看到Application
、IndexManager
两个属性,会不知所措: 该不该给他们赋值呢?这两个玩意是干吗的呢?
事实上,这两个属性的set
块,在 SearcherBase
类型初始化的时候才会被用到。
public class SearcherBuilder
{
private IocResolver _iocResolver;
public SearcherBase Build(IApplication app, IIndexManager manager)
{
SearcherBase searcher = _iocResolver.Resolve(app.SearcherType) as SearcherBase;
searcher.Application = app,
searcher.IndexManager = manager;
return searcher;
}
}
将领域模型的属性set
块暴露出来,会造成属性的语义不明,没有表达出业务逻辑(初始化),不符合DDD有关领域模型设计的要求。因此,可以根据情况对SearcherBase
做如下两种方式的重构:
- 尽可能多的在构造函数中初始化内部成员
public class SearcherBase
{
public SearcherBase(IApplication app, IIndexManager manager)
{
Application = app,
IndexManager = manager;
}
public IApplication Application { get; }
public virtual IIndexManager IndexManager { get; }
public virtual async Task<SearchResult> SearchAsync(Query query)
{ //some code }
}
- 对于该例,由于关系到通过容器初始化实例,可以创建
SearcherBase
的初始化方法
public class SearcherBase
{
public void Initialize(IApplication app, IIndexManager manager)
{
Application = app;
IndexManager = manager;
}
public IApplication Application { get; private set;}
public virtual IIndexManager IndexManager { get; set;}
public virtual async Task<SearchResult> SearchAsync(Query query)
{ //some code }
}
public class SearcherBuilder
{
private IocResolver _iocResolver;
public SearcherBase Build(IApplication app, IIndexManager manager)
{
SearcherBase searcher = _iocResolver.Resolve(app.SearcherType) as SearcherBase;
searcher.Initialize(app, manager);
return searcher;
}
}
经过上述重构,当客户端在看到 SearcherBase
的时候,会发现 Application
、IndexManager
两个属性并没有Set
块,并根据第二种重构方式的Initialize
方法推断出,类型初始化的时候,两个属性被赋值。
这样,就增强了SearcherBase
的可读性。
何时进行类似重构?
《实现领域驱动设计》这本书中提到,DDD关注的是 核心复杂领域模块 的设计。整个项目中的各个模块有轻重主次之分,只有那些多次被引用、关系核心业务环节的模块才应该做精心的设计,否则会造成过度设计。