Herb Sutter 曾经有一个观点, 就是一个组件的接口, 不只包括这个组件本身定义的方法, 还包括使用这个组件的客户代码, 比如以这个组件为参数的那些方法. 扩展方法是对Herb Sutter这个观点所做的语法上的支持: 把以这个组件为参数的那些客户代码转变成扩展方法后, 调用时从语法上看起来跟调用这个组件本身定义的方法一模一样了!
---------------------现实的分割线------------------------
上面只是一厢情愿的历史溯源. 真实的情况是扩展方法是为了 LINQ 而加进来的, 就为了是表达式看起来干净漂亮些: videoList.Where(video => video.Rate > 18).Select(video => video.Title);
然而这种语法上的便利和优雅, 鼓励了一种原本在面向对象设计中就争论颇多的设计元素的更加随意的使用: 静态方法. 扩展方法只不过是抹了糖的静态方法.
我们需要整理一下扩展方法的应用场景.
考察一下LINQ. LINQ 为 IEnumerable 添加的扩展方法有几个特点:
是业务无关的通用算法. 无论你是开发桌面应用还是web app, 无论是金融电信等行业软件, 还是博客聊天等个人工具, 都可以从这些通用的扩展方法中受益.
是全局的, public的. 这应该是上一个特点带来的不得不这样的一种选择.
目的是表达式的可读性
LINQ是对扩展方法的一种成功应用, 也是我所知的唯一一种大范围使用的应用. 鉴于扩展方法也原本是为LINQ而引入的, 所以你可以认为LINQ所代表的应用场景就是扩展方法应该的应用场景. 项目中较为成功的扩展方法应用是为 object 扩展了 ShouldBe 方法用于测试中, 比如:
public static void ShouldBe(this object actual, object expected)
{
Assert.Equals(actual, expected);
}
用起来是 actual.ShouldBe(expected); 完全满足上面的三个特性.
再考察一下工作中曾经遇到的有争论的扩展方法的应用场景
最争议的是业务相关的逻辑应该不应该用扩展方法来表达, 业务模型应该不应该用扩展方法来扩展? General的回答是不应该. 毕竟是静态方法, 意味着某种业务概念的缺失, 应该有更好的封装方法. 但不妨以扩展方法开始: 曾经发生过的例子是我们最开始为HttpRequest创建了一个我们的业务相关的扩展方法, 而在接下来的几周内我们不断的为HttpRequest添加业务相关的扩展方法, 最终我们发现这几个方法合在一起其实是我们的一个领域概念.
在从扩展方法过渡到领域模型的过程中, 有一些跟上面几条不一样的规则:
扩展方法应该是internal的, 因为是业务相关的, 必然只在当前Context下有效, 我们不希望在其它Context下它们也被IDE的智能提示带出来
扩展方法所在的类不应该以所扩展的类或接口来命名, 比如不应该叫 HttpRequestExtensions, StringExtensions, 而应该以业务概念来命名. 这样更meaningful, 以后的过渡也会比较自然
扩展方法应该放在靠近使用它的地方, 而不是放在它所扩展的类或接口旁边. 这是第一条规则的推论, 也是Herb Sutter理论的推论, 因为这种场景下的扩展方法的实现代码其实原本是客户代码, 本来就应该放在使用它的地方.
从这个角度来说, 扩展方法的出现是一个信号, 意味着所扩展对象的一个新的Context出现了. 因此扩展方法和 DCI 可以看作是对同一个问题的两种不同解答, 两者某种程度上都是为了解决可读性.
最后说个题外话, 扩展方法比一般实例方法有一个好处, 就是可以对象实例为null时可以不必抛出NullReferenceException, 因为此时对象实例是作为参数传进来, 可以做判断给出更有意义的异常.
分享到:
相关推荐
可以使用`php -i`查看已安装的扩展,`php -r`运行单行代码测试,以及`php -d display_errors=On`显示错误信息,帮助调试扩展。 9. **面向对象编程**: PHP 5引入了全面的面向对象支持,C++扩展也可以利用这一特性...
你需要在Xcode项目中添加一个新的目标,选择“Input Method Extension”模板。这个扩展将作为键盘的实际实现。 2. **UIKeyboardType**: 在自定义键盘中,你可以使用`UIKeyboardType`枚举来设置不同的键盘布局,包括...
*Introduce Local Extension 引入本地扩展类 Chapter 8:Organizing Data 组织数据 Self Encapsulate Field 自封装字段 Replace Data Value with Object 用对象代替数据值 Change Value to ...
*Introduce Local Extension 引入本地扩展类 Chapter 8:Organizing Data 组织数据 Self Encapsulate Field 自封装字段 Replace Data Value with Object 用对象代替数据值 Change Value to ...
*Introduce Local Extension 引入本地扩展类 Chapter 8:Organizing Data 组织数据 Self Encapsulate Field 自封装字段 Replace Data Value with Object 用对象代替数据值 Change Value to ...
*Introduce Local Extension 引入本地扩展类 Chapter 8:Organizing Data 组织数据 Self Encapsulate Field 自封装字段 Replace Data Value with Object 用对象代替数据值 Change Value to ...
*Introduce Local Extension 引入本地扩展类 Chapter 8:Organizing Data 组织数据 Self Encapsulate Field 自封装字段 Replace Data Value with Object 用对象代替数据值 Change Value to ...
8. **Category与Extension**:Objective-C允许通过Category来扩展已有类的功能,而Extension则可以为类提供私有实现。 9. **NSPredicate和KVC/KVO**:NSPredicate用于过滤和查询数据,Key-Value Coding (KVC)和Key-...
8. **事件方法(event method)**:事件方法是专门为特定事件设计的函数,如On_Click,只在该事件发生时被调用。 9. **异常(exception)**:异常是在程序运行时出现的错误或异常情况,会导致程序中断。异常处理允许...
该函数用于动态加载扩展。 - **默认值**:默认关闭 (`Off`),尤其是在多线程服务器环境中。 - **示例**:`enable_dl = On` - **注意事项**:在多线程的服务器上(如 IIS 或 Zeus),`dl()` 函数可能无法正常工作...
print("Server started on port \(server?.localPort ?? 0)") } catch { print("Error starting server: \(error.localizedDescription)") } } func stopServer() { server?.stop() } } ``` 这里的`...
- **扩展**: `public`表示该方法可以被任何类访问;`static`表示无需实例化类即可调用该方法;`String[] args`是接收命令行参数的数组。 ##### 题目7: Which of the following statements is correct? - **选项**: ...
- `zend_extension`:指定Xdebug DLL文件的路径。 - `xdebug.profiler_enable`:开启性能剖析功能。 - `xdebug.remote_enable`:开启远程调试功能。 - `xdebug.auto_trace`:开启自动追踪功能。 - `xdebug....
解决这个问题的方法非常直接:你需要找到`php.ini`文件,然后将`extension_dir`的值更改为实际的PHP扩展库目录。例如,如果PHP安装在`D:/myphpenv/php5.4/`,那么你应该将配置行: ```ini ; extension_dir = "ext" ...
使用`<mime-mapping>`元素定义文件扩展名与其对应的MIME类型,如`<mime-mapping><extension>pdf</extension><mime-type>application/pdf</mime-type></mime-mapping>`。 13. **定位TLD** `<taglib>`元素用于指定...
- **CompressionMethod**:使用的压缩方法。 - **CreationClassName**:创建此对象的类名。 - **CreationDate**:文件创建日期。 - **CSCreationClassName**:计算机系统的创建类名称。 - **CSName**:计算机系统的...
通过上述步骤,你已经掌握了AspUpload组件的基本使用方法。在实际项目中,你可以根据需求扩展功能,如文件重命名、多文件上传、文件预览等。AspUpload组件的强大之处在于它的灵活性和易用性,可以帮助开发者快速构建...
- **404.2 Web Service Extension Lockdown Policy Prevents Request**:Web服务扩展锁定策略阻止本请求。 - **404.3 MIME Mapping Policy Prevents Request**:MIME映射策略阻止本请求。 - **405 Method Not ...
这里的`extension`参数定义了视图扩展名,默认设置为`.jtml`。`config`参数则指定了框架的主要配置文件路径。 #### 三、dingframeworkMVC的配置 dingframeworkMVC的配置主要通过`ding-servlet.xml`文件完成,该...