实现步骤
其实这也是个比较简单的功能,于是我一开始便用最直接的方式进行开发:
public class SearchCriteriaBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var modelName = bindingContext.ModelName; var rawValue = bindingContext.ValueProvider[modelName].RawValue; var text = HttpUtility.UrlDecode(rawValue.ToString()); var tokenGroups = this.Tokenize(text); ... } private List<string[]> Tokenize(string text) { ... } } |
这个Model Binder会从Value Provider中得到Model Name(一般Action参数的名称,在这里不是重点)所对应的rawValue,经过了URL Decode之后便得到了text,它是一个带有信息的字符串,也便是《趣味编程》所要解析的对象。如上面的例子,text便是:
keywords-hello world--price-10-20--color-black-red |
请注意,原本在URL中表示为%20的字符,已经被URL Decode为一个空格。在得到text之后,我便要将其拆分为一个List<string[]>对象,这便是分割好的结果。拆分字符串的逻辑比较复杂,因此我将其提取到一个独立的Tokenize方法中去。于是我接下来就开始实现Tokenize方法了,写啊写,写完了。但是,我到底写的正不正确?我不知道。我唯一知道的东西是,这个逻辑不简单,我需要测试一下才放心。因此,在继续其他工作之前,我想要为它写一些单元测试。
这就是涉及到一个问题,我们该如何为一个私有方法作单元测试呢?我以前也想在博客上讨论这个问题,但是最终不知为何没有进行。我的看法是,如果设计得当,每个类的职责单一,应该不会出现需要进行单元测试的私有方法。如果一个私有方法需要测试,那么说明它的逻辑相对较为复杂,而且有独立的职责,应该将其提取到外部的类型中。例如在这里,Tokenize方法便值得我这样么做——因为我想要单元测试。于是我提取出一个Tokenizer抽象,以及一个默认的逻辑实现:
internal interface ITokenizer { List<string[]> Tokenize(string text); } internal class Tokenizer : ITokenizer { public List<string[]> Tokenize(string text) { ... } } |