第 6 章:扩展性设计
6.1 扩展机制
考虑用不包含任何虚成员或受保护的成员的非密封类来为框架提供扩展性。这种方法所提供的扩展性广受用户欢迎,而且它的开销也不高。
考虑将受保护的成员用于高级的定制方案。
要在对安全性、文档及兼容性进行分析时,把非密封类中受保护的成员当做公有成员那样来对待。
考虑使用回调函数来允许用户向框架提供自定义的代码供框架执行。
考虑使用事件来允许用户对框架的行为进行定制,这样就不需要用户对面向对象设计有深入的了解。
要优先使用事件,而不是简单的回调函数,其原因在于广大开发人员更熟悉事件,而且事件与 VS 的语句自动完成特性结合得很好。
避免在对性能要求很高的 API 中使用回调函数。
要在定义用了回调函数的 API 时,使用新的 Func<...>、Action<...> 或 Expression<...> 类型,而不要使用自定义的委托。
要在用 Expression<...> 来代替 Func<...> 和 Action<...> 委托的时间进行测量,从而了解它可能对性能产生的影响。
要理解在调用委托时可以执行任何代码,这可能会引起安全性、正确性及兼容性的问题。
不要使用虚成员,除非有合适的理由,而且对设计、测试及维护虚成员的开销有清楚地认识。
要优先使用受保护的虚成员,而不是公有虚成员。公有成员应该通过调用受保护的虚成员的方式来提供扩展性(如有必要)。
不要提供抽象,除非为该抽象开发出多个具体实现并通过用到该抽象的 API 对其进行过的实际测试。
要在设计抽象时谨慎地选择抽象类或接口。
考虑为抽象的具体实现提供参考测试。这类测试应该能告诉用户,他们是否正确地实现了契约。
6.2 基类
考虑将基类定义为抽象类,即使他们并不包含任何抽象成员。这能够清楚地告诉用户,设计这些类的目的完全是为了让用户使用它们来派生自己的子类。
考虑把基类与用于主要场景的类型分开,并放到单独的命名空间中。根据定义,基类是为了高级的扩展场景而设计的,因为大多数用户对它们并不感兴趣。
避免在命名基类时使用“Base”后缀 - 如果公共 API 会用这个类。
6.3 密封
不要把类密封起来,除非有恰当的理由。
不要在密封类中声明受保护的成员或虚成员。
考虑在覆盖成员时将其密封。