第6章 扩展性设计
6.1 扩展机制
6.1.1 非密封类
-
CONSIDER
: 考虑 用非密封类(且不包含任何虚成员、受保护成员)为框架提供扩展性。这种类提供的扩展性广受用户欢迎,且开销不高。
6.1.2 受保护的成员
-
CONSIDER
:受保护的成员用于 高级 的定制方案。用在继承中,避免使 公共 接口变得过于复杂。
-
DO
:在对安全性、文档、兼容性进行分析时,把 非密封 类中受保护的成员当作 公有 成员来对待。毕竟用户是可以访问到 protected 成员的。
6.1.3 事件与回调函数
-
CONSIDER
:建议使用回调(事件),这允许用户 提供可以被框架执行的自定义代码 。
-
CONSIDER
:建议 使用事件而 非 虚成员,这允许用户在无需深入理解面向对象设计的情况下自定义框架的行为。
-
CONSIDER
:优先 使用事件,而 非 简单的回调函数。事件与 Visual Studio 的语句自动完成特性结合的很好,更易用。
-
AVOID
: 避免 在性能要求很高的 API 中使用回调。
-
DO
:基于委托的 API 应该使用 Func<...>
、 Action<...>
或 Expression<...>
类型,而非自定义委托。
Expression
代表可以在运行时被编译并随后被调用的函数定义,同时也可以被序列化并传递给远程进程。Expression
是用于分析代码的,Func
/Action
是用于运行代码的。
-
DO
:评估或理解性能的影响,应使用 Expression<...>
;。除此之外,应使用 Func<...>
和 Action<...>
委托。Expression
可能会提高性能(如 LINQ to SQL),也可能降低性能(如简单的计算机或操作,它会增加编译/解释的开销)。
-
DO
:要理解:调用委托时可以执行任何代码,这可能会引起 安全 性、 正确 性及 兼容 性问题。
6.1.4 虚成员
-
DON'T
:除非有合适的理由, 不要 使用虚成员。虚成员的设计、测试及维护开销较高,在不破坏兼容性的前提下很难对虚成员进行修改。因编译器无法 内联 ,虚成员的效率相比非虚成员低。
-
CONSIDER
:建议使用 Template Method(模板方法) 模式,将可扩展性限制在绝对必要的范围内。
-
DO
:优先扩展受 保护 的虚成员,而不是 公有 虚成员。公有成员应该通过调用 受保护 的虚成员来提供扩展性(如果有必要)。public MyClass{ public void PrintSomething(string content){ ... PrintSomething(content, format); } public void PrintSomething(string content, string format){ ... PrintSomething(content, format, false); } protected virtual void PrintSomething(string content, string format, bool isCapital){ ... // 实际代码 } }
6.1.5 抽象(抽象类和接口)
-
DON'T
:不要提供抽象,除非已经通过开发一些具体的实现以及用于调用它们的 API 测试过这些抽象。在发布抽象前,应该至少构建两到三个不同的实现,在两到三个不同的消费者(开发者)中进行过验证。
-
DO
:设计抽象时应谨慎选择抽象类或是接口。具体细节见 4.3class和interface之间的选择
-
CONSIDER
:为抽象的具体实现提供 参考测试 。这类测试应该能告诉用户,他们是否正确的实现了契约。
6.2 基类
本书所述基类,是指:
基类的设计目的是为了提供一个共同的 抽象 ,或是完成一些 默认 实现,帮助用户实现抽象。基类通常位于继承层次的 中 部,低于 顶 部(抽象),高于 底 部(自定义实现)。
-
CONSIDER
:将基类定义为 抽象 类,即使它们不包含任何抽象成员。目的是告诉用户,这些类的目的完全是用于 派生 。
-
CONSIDER
:基类是为高级场景设计的,考虑将其放在单独的 namespace 中。详见2.2.4 分层架构原则,此处的基类是指通用的实现,如
CollectionBase
,它们通常不会被开发者使用。
-
AVOID
:如果公共 API 中会用到这个基类,不要使用“ Base ”后缀。例如:
Collection<T>
的设计目的是作为基类使用,在许多情况下,框架暴漏的 API 仍会使用它,而不是它的派生类。// 因Collection<T>是公开的,因此我们可以定义Collection<T>成员,此时CollectionBase<T>的名字便不合适 public void Print(Collection<T> collection) { ... } public void Print(List<T> collection) { ... }
6.3 密封
-
DON'T
:除非有恰当的理由,否则不要把类密封起来。把类密封起来的恰当理由如下:
- 静态 类。
- 类的受保护成员保存了 需要高度保密的机密信息 。
- 类继承了许多 虚成员,把这些成员分别密封的代价太高,不如密封整个类 。
- 类是 特性(Attribute) ,需要在运行时能快速查找。
-
DON'T
:密封类中不应该声明 protected 成员或 virtual 成员。
-
CONSIDER
: 建议 密封覆写成员(override)。public class FlowSwitch : SourceSwitch { protected sealed override void OnValueChanged(){ ... } }
标签:CONSIDER,成员,扩展性,密封,抽象,基类,设计 From: https://www.cnblogs.com/hihaojie/p/18664900/chapter-6-expansion-design-1nx1eq