我又想测试GetConverter方法了,怎么办?那就还是把GetConverter这部分逻辑从外部注入吧——哎,难道还要我写一个IConverterFactory接口和ConverterFactory类吗?也不一定,我们还是用“轻量”些的方法吧:
public class SearchCriteriaBinder : IModelBinder { ... public SearchCriteriaBinder() : this(new Tokenizer(), GetConverter) { } internal SearchCriteriaBinder(ITokenizer tokenizer, Func<string, IConverter> converterGetter) { this.m_tokenizer = tokenizer; this.m_getConverter = converterGetter; } private readonly ITokenizer m_tokenizer; private readonly Func<string, IConverter> m_getConverter; internal static IConverter GetConverter(string field) { // 使用if ... else或是字典 } private SearchCriteria Build(List<string[]> tokenGroups) { ... if (fieldTokens.TryGetValue("keywords", out values)) { searchCriteria.Keywords = (string)this.m_getConverter("keywords").Convert(values); } ... } } |
这一次,我使用了委托对象的方式注入一段逻辑,它其实也是我们可以使用的一种方式。这样,我们便可以为GetConverter方法作独立的单元测试了。不过,我这里使用委托也有点“展示”的意味在里面,在实际开发过程中,可能我还是会使用ConverterFactory,这对我来说更“正规”一些。这种“接口”与“实现”分离的做法,除了能够独立测试之外,还有一个目的就是为了在测试Build方法时不依赖GetConverter的实现,也意味着不依赖PriceRangeConverter,KeywordConverter的实现等等,因为我们在测试Build方法时,可以提供一个针对测试的GetConverter方法逻辑,返回一些IConverter的Mock或Stub对象。这样,Build方法也就非常独立,不依赖外部实现了。