I have a specification that validates codes. It looks like the following:
public ClassificationSpecification : ISpecification<Classification> {
HashSet<string> codes;
// constructor elided
public IsSatisfiedBy(Classification classification) {
return codes.Contains(classification.Code);
}
}
The valid codes come out of a Classification
table in a database. My question is, which is the better constructor for dependency injection?
public CodeSpecification(IEnumerable<string> codes) {
this.codes = new HashSe开发者_运维问答t<string>(codes);
}
or
public CodeSpecification(IRepository<Classification> repository) {
this.codes = new HashSet<string>(repository.Select(x => x.Code));
}
And the all important question: why?
Your second constructor does real work (via the repository) and this is a bad idea. See http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/
You need to decide if it's valid to load all the values (pass in the values you need to the constructor), or you want to look them up on each call (pass in the repository in the constructor and store it).
I would use this constructor:
private readonly IRepository<Classification> _repository;
public CodeSpecification(IRepository<Classification> repository)
{
_repository = repository;
}
Then, find the valid codes when your class is actually called upon:
public bool IsSatisfiedBy(Classification classification)
{
var _repository.Any(x => x.Code == classification.Code);
}
This ensures that no real work is done until it is needed, separating your application's initialization from its runtime. It also ensures that you are always working with fresh data in IsSatisfiedBy
; caching the values in the constructor introduces a period of time where the codes in the repository might change.
If the number of codes is large, and the repository won't execute the Any
efficiently, you may still want to implement caching. It is a good idea to follow the same advice and not cache until the first call to IsSatisfiedBy
:
private readonly HashSet<string> _codes;
private readonly object _codesSync = new object();
public bool IsSatisfiedBy(Classification classification)
{
if(_codes == null)
{
lock(_codesSync)
{
if(_codes == null)
{
_codes = new HashSet<string>(_repository.Select(x => x.Code));
}
}
}
return _codes.Contains(classification.Code);
}
Constructors in dependency-injected objects are infrastructure elements; domain logic of any kind should generally be deferred until the object is invoked by another.
精彩评论