《.NET 设计规范》第 9 章:常用的设计模式

第 9 章:常用的设计模式

9.1 聚合组件

  考虑为常用的特性域提供聚合组件。

  要用聚合组件来对高层的概念(物理对象)进行建模,而不是对系统级的任务进行建模。

  要让聚合组件的名字与众所周知的系统实体相对应,比如 MessageQueue、Process 或 EventLog,这样就能使类型更加引人注目。

  要在设计聚合组件时使初始化尽可能地简单,这样用户只需进行简单的初始化就可以使用组件。如果某一项初始化是必需的,那么由于没有对组件进行初始化而引发的异常应该明确地告诉用户应该怎么做。

  不要要求聚合组件的用户在一个场景中显式地实例化多个对象。

  要保证让聚合组件支持 Create-Set-Call 使用模式,这样用户就可以先实例化组件,然后设置它的属性,最后调用一些简单的方法,以实现大多数场景。

  要为所有的聚合组件提供默认构造函数或非常简单的构造函数。

  要为聚合组件提供可读写的属性来与构造函数中的所有参数相对应。

  要在聚合组件中使用事件,不要使用基于委托的 API。

  考虑用事件来代替需要被覆盖的虚成员。

  不要要求聚合组件的用户在常用场景中使用继承、覆盖方法及实现接口。

  不要要求聚合组件的用户在常用场景中除了编写代码之外,还要做其他的做工。例如,不应该让用户用配置文件来配置组件,也不应该让用户生成资源文件,等等。

  考虑让聚合组件能够自动切换状态。

  不要涉及有多种状态的因子类型。

  考虑将聚合组件集成到 VS 的设计器中。

  考虑把聚合组件和因子类型分开,各自放在不同的程序集中。

  考虑把聚合组件内部的因子类型暴露给外界访问。

9.2 Async 模式

  要实现基于事件的 Async 模式 - 如果类型是一个支持可视化设计器的组件(也就是说类型实现了 IComponent)。

  要实现经典的 Async 模式 - 如果必须支持等待句柄。

  考虑在实现高层 API 时使用基于事件的 Async 模式。例如,聚合组件就应该实现该模式。

  考虑在实现底层 API 时使用经典的 Async 模式,在这种情况下更强大的功能、更少的内存消耗、更好的灵活性、更少的磁盘占用要比可用性更重要。

  避免在同一个类型中甚至是一组相关的类型中同时实现两种 Async 模式。

  要在为异步操作定义 API 时遵循下面的约定。给定名为 Operation 的同步方法,应该提供名为 BeginOperation 和 EndOperation 的方法,它们的方法签名如下面所示(注意,输出参数不是必需的)。

  要确保 Begin 方法的返回类型实现了 IAsyncResult 接口。

  要确保同步方法的按值传递和按引用传递的参数在 Begin 方法中都是按值传递的。同步方法的输出参数不应该出现在 Begin 方法的签名中。

  要确保 End 方法的返回类型与同步方法的返回类型相同。

  要确保同步方法的任何输出参数和按引用传递的参数都作为 End 方法的输出参数。同步方法中安置传递的参数不应该出现在 End 方法的签名中。

  不要继续执行异步操作 - 如果 Begin 方法抛出了异常。

  要一次通过下面的机制来通知调用方异步操作已经完成。

    将 IAsyncResult.IsCompleted 设为 true。

    激活 IAsyncResult.AsyncWaitHandle 返回的等待句柄。

    调用异步回调函数。

  要通过从 End 方法中抛出异常来表示无法成功地完成异步操作。

  要在 End 方法被调用时同步完成所有尚未完成的操作。。

  考虑抛出 InvalidOperationException 异常 - 如果用户用同一个 IAsyncResult 两次调用 End 方法,或 IAsyncResult 是从另一个不相关的 Begin 方法返回的。

  要把 IAsyncResult.CompletedSynchronously 设为 true - 当且仅当异步回调函数将在调用 Begin 方法的线程中运行的时候。

  要确保在正确的线程中调用事件处理程序。与经典 Async 模式相比,这是使用基于事件的 Async 模式的主要好处之一。

  要确保无论是操作已经完成,还是操作出错,还是操作被取消,都是种会调用事件处理程序。不应该让应用程序无休止地等待一间永远不会发生的事件

  要确保在异步操作失败后,访问时间参数类的属性会引发异常。换句话说,如果有错误导致操作无法完成,那么就不应该允许用户访问操作的结果。

  不要为返回值为空的方法定义新的事件处理程序或事件参数类型。要使用 AsyncCompletedEventArgs,AsyncCompletedEventHandler 或 EventHandler<AsyncCompletedEventArg>。

  要确保如果在一个一步操作中实现了 PaogressChanged 事件,那么在操作的完成事件被触发之后,不应该再出现此类事件。

  要确保如果使用了标准的 ProgressChangedEventArgs,那么 ProgressPercentage 始终能用来表示进度的百分比(不一定要完全精确,但表示的一定要百分比)。如果使用的不是标准进度,那么从 ProgressChangedEventArgs 派生一个子类会更合适,这种情况下应该保持 ProgressPercentage 为 0 ;

  要在有增量结果需要报告的时候出发 ProgressChanged 事件。

  要对 ProgressChangedEventArgs 进行扩展来保存增量结果数据,并用扩展后的时间参数类来定义 ProgressChanged 事件。

  要把增量结果报告与进度报告分开。

  要为每个异步操作定义单独的 <MethodName>ProgreessChanged 事件和相应的事件参数类,来处理该操作的增量结果数据。

  

9.3 依赖属性

  要提供依赖属性 - 如果需要用他们来支持各种 WPF 特性,比如样式、触发器、数据绑定、动画、动态资源以及继承。

  要在设计依赖属性的时候继承自 DependencyObject 或它的子类型。该类型实现的属性存储区非常高效,它还自动支持 WPF 的数据绑定。

  要为每个依赖属性提供常规的 CLR 属性和存放 System.Windows.DependencyProperty 实例的公有静态只读字段。

  要通过调用 DependencyObject.GetValue 和 DependencyObject.SetValue 的方式来实现依赖属性。

  要用依赖属性的名字加上“Property”后缀来命名依赖属性的静态字段。

  不要显式地在代码中设置依赖属性的默认值,应该在元数据中设置默认值。

  不要在属性的访问器中添加额外的代码,而应该使用标准代码来访问静态字段。

  不要使用依赖属性来保存保密数据。任何代码都能访问依赖属性,即使它们是私有的。

  不要把依赖属性的验证逻辑放在访问器中,而应该把验证毁掉函数传给 DependencyProperty.Register 方法。

  不要在依赖属性的访问器中实现属性改变的通知,而应该向 PropertyMetadata 注册改变通知的回调函数,后者是依赖属性本身提供的一项特性,为了支持改变通知,必须使用该特性。

  不要在依赖属性的访问器中实现属性强制赋值逻辑,而应该向 PropertyMetadata 注册强制赋值的回调函数。后者是依赖属性本身提供的一项特性,为了支持强制赋值,必须使用该特性。

  

9.4 Disopse 模式

  要为含有可处置类型实例的类型实现基本 Dispose 模式。

  要为类型实现基本 Dispose 模式并提供终结方法 - 如果类型持有需求由开发人员显式释放的类型,而且后者本身没有终结方法。

  考虑为类实现基本 Dispose 模式 - 如果类本身并不持有非托管资源或可处置对象,但是它的子类型却可能会持有非托管资源或可处置对象。

  要按下面的方法来实现 IDisposable 接口,即先调用 Dispose(true),然后再调用 GC.SuppressFinalize(this)。

  不要将无参数的 Dispose 方法定义为虚方法。

  不要为 Dispose 方法声明除了 Dispose() 和 Dispose(bool) 之外的任何其它重载方法。

  要允许多次调用 Dispose(bool) 方法。他可以在第一次调用之后就什么也不做。

  避免从 Dispose(bool) 方法中抛出异常,除非是紧急情况,所处的进程已经遭到破坏(比如泄漏、共享状态不一致,等等)。

  要从成员中抛出 ObjectDisposedException 异常 - 如果该成员在对象终结之后就无法继续使用。

  考虑在 Dispose() 方法之外在提供一个 Close() 方法 - 如果 close 是该领域中的一个标准术语。

  避免定义可终结类型。

  不要定义可终结的值类型。

  要将类型定义为可终结类型 - 如果该类型要负责释放非托管资源,且非托管资源本身不具备终结方法。

  要为所有的可终结类型实现基本 Dispose 模式。

  不要在终结方法中访问任何可终结对象,这样做存在很大的风险,因为被访问的对象可能已经被终结了。

  要将 Finalize 方法定义为受保护的。

  不要在终结方法中放过任何异常,除非是致命的系统错误。

  考虑创建一个用于紧急情况的可终结对象 - 如果终结方法在应用程序域被强制卸载或线程异常退出的情况下都务必要执行。

9.5 Factory 模式

  要优先使用构造函数,而不是优先使用工厂,因为与特殊的对象构造机制相比,构造函数一般来说更容易使用、更一致,也更方便。

  考虑使用工厂 - 如果构造函数提供的对象创建机制不能满足要求。

  要使用工厂 - 如果开发人员可能不清楚待创建的对象的确切类型,比如对基类或接口编程就属于这种情况。

  考虑使用工厂方法 - 如果这是让操作不言自明的唯一方法。

  要在转换风格的操作中使用 factory。

  要尽量将工厂操作方法实现为方法,而不是实现为属性。

  要通过方法的返回值而不是方法的输出参数来返回新创建的对象实例。

  考虑把 Create 和要创建的类型名连在一起,一次来命名工厂方法。

  考虑把要创建的类型名和 Factory 连在一起,一次来命名工厂类型。例如,可以考虑把创建 Control 对象的工厂类型命名为 ControlFactory。

  

9.6 对 LINQ 的支持

  要实现 IEnumerabl<T>,其目的是为了得到基本的 LINQ 支持。

  考虑实现 ICollection<T>,其目的是为了提高查询的性能。

  考虑实现 IQueryable<T> - 如果必须要访问传给 IQueryable 的成员的查询表达式。

  不要草率地实现 IQueryable<T>,要理解这样做可能会对性能产生什么影响。

  要在 IQueryable<T> 的方法中抛出 NotSupportedException - 如果你的数据源上不支持该操作。

  要在新类型中将 Query 模式实现为实例方法 - 如果在 LINQ 以外的场合,这些方法在类型中仍然有存在的意义。否则,应该将它们实现为扩展方法。

  要让实现了 Query 模式在类型实现了 IEnumerable<T>。

  考虑在设计 LINQ 操作符时,让它们返回领域特有的可枚举类型。虽然从本质上来说,Select 查询方法可以返回任何类型,但是大家通常都希望查询的结果是可枚举类型。

  避免只实现 Query 模式的一部分 - 如果不希望退回到基本的 IEnuerable<T> 实现。

  要为有序序列定义单独的类型,从而将它和对应的无序序列分开。这样的类型应该定义 ThenBy 方法。

  要推迟执行实际的查询操作。对 Query 模式的大多数成员来说,我希望它们只是创建一个新的对象,并在枚举的时候才产生集合重负荷查询条件的元素。

  要将用于查询的扩展方法放在主命名空间中的一个名为“Linq” 的子命名空间中。例如,为 System.Data 特性定义的扩展方法被放在 System.Data.Linq 命名空间。

  要在参数中使用 Expression<Func<...>>,而不是 Func<...> - 如果需要查询查询表达式。

9.7 Optional Feature 模式

  考虑将 Optional Feature 模式用于抽象中的可选特性。

  要提供一个简单的布尔属性来让用户检测对象是否支持可选特性。

  要在积累中将可选特性定义为虚方法,并在该方法中抛出 NotSupportedException 异常。

  

9.8 Simulated Convariance 模式

  考虑使用 Simulated Convariance 模式 - 如果需要有一种统一的类型来表示泛型类型的所有实例。

  要确保以等价的方式来实现根基类型成员和对应的泛型类型成员。

  考虑使用抽象基类来表示根基类型,而不是使用接口来表示根基类型。

  考虑用非泛型类型作为根基类型 - 如果这样的类型已经存在。

  

9.9 Template Method 模式

  避免将公有成员定义为虚成员。

  考虑使用 Template Method 模式来更好地控制扩展性。

  考虑以非秀成员的名字加“Core”后缀为名字,来命名为该费虚成员提供扩展点的受保护的虚成员。

  

9.10 超时

  要优先让用户通过参数来制定超时长度。

  要优先使用 TimeSpan 来表示超时长度。

  要在超时后抛出 System.TimeoutException 异常。

  不要通过返回错误码的方式来告诉用户发生了超时。

  

9.11 可供 XAML 使用的类型

  考虑提供默认构造函数 - 如果想让类型能用于 XAML。

  要提供标记扩展 - 如果想让 XAML 读取程序能够创建不可变的类型。。

  避免定义新的类型转换器,除非这样的转换是自然而直观的。一般来说,应该将类型转换器的使用范围限制在 .NET 框架中已经使用了类型转换器的地方。

  考虑将 ContentPropertyAttribute 用于最常用的属性,从而得到更方便的 XAML 语法。

时间: 2024-10-24 23:21:46

《.NET 设计规范》第 9 章:常用的设计模式的相关文章

iOS开发中常用的设计模式

常用的设计模式(一)代理模式应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现.优势:解耦合敏捷原则:开放-封闭原则实例:tableview的 数据源delegate,通过和protocol的配合,完成委托诉求.列表row个数delegate自定义的delegate (二)观察者模式应用场景:一般为model层对,controller和view进行的通知方式,不关心谁去接收,只负责发布信息.优势:解耦合敏捷原则:接口隔离原则,开放-封闭原则实例:Notificatio

iOS常用的设计模式

iOS中常用的设计模式 ( 1 ) 代理模式 应用场景: 当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现. 优势: 敏捷原则: 开放 - 封闭原则 实例:tableView的 数据源delegate,通过和protocol的配合,完成委托诉求. 列表row个数delegate自定义的delegate ( 2 ) 观察者模式 应用场景:一般为model层对,controller和view进行的通知方式,不关心谁去接收,只负责发布信息. 优势: 解耦合 敏捷原则: 接口隔离原则

oracle学习 第三章 常用的SQL*PLUS命令 ——02

今天接着昨天的RUN命令继续讲. 3.5 n(设置当前行)命令和A(PPEND)(附加)命令 设想,你输入了例3-10的查询语句 例 3-10 SQL> SELECT ename 2 FROM emp; 例 3-10 结果 看到以上输出时,您发现在SELECT子句中忘了job,sal.这时您又如何修改您的SELECT子句呢?首先您应该使用SQL*PLUS的L(LIST)命令来显示SQL缓冲中的内容. 例 3-11 SQL> L 例 3-11 结果 在例3-11显示的结果中,2后面的"

【Oracle】第二章常用操作

ORACLE 第二章常用操作 修改表中的列信息: alter table vendor_master modify(vencode varchar(20)); 给表中列添加一个默认值约束 alter table mytable modify(name varchar2(20) default 'abc'); 为现有表中的列添加一个主键约束: alter table testtable add(constraint "idkey" primary key("ID"))

最常用的设计模式(单例模式)

记得刚开始涉足程序的时候, 去笔试 ,发现有一个笔试题经常粗线,写一个单例模式的基本实现, 当时没研究设计模式也就不知为何物, 到今日  , 才发现它已成为我日常开发最常用的一种设计模式. 我写的所有设计模式的代码都会用java 呈现, 虽然第一个学习的是c++但是 最开始作为工作的是java,并且有点偏好java 单例模式 , 意思就是 整个系统仅只有此类的一个实力, 当然这只是狭义的单例,经常看到变种的单例是允许,创建指定数量的实例的 单例模式是一种创建型模式. 它是优化的一种策划, 避免重

第3章 常用运算符

第3章 常用运算符 1.什么是运算符 Java中的运算符常见的有以下五种:? 算术运算符? 赋值运算符? 比较运算符? 逻辑运算符? 条件运算符下面分别进行说明 2.算数运算符 算术运算就是指加减乘除这些,不过需要说明的还是有两种的:1.求余%符号表示求余运算,也既是模运算,得出的结果是一个余数2.自增和自减++和-分别表示自增和自减,在实际使用中要注意符号是在变量的左边还是右边.左边是先自增后使用,右边是先使用后自增两个例子:int a = 5;int b = ++a;返回的结果是:a=6,b

几种常用的设计模式介绍

几种常用的设计模式介绍                                         1.    设计模式的起源 最早提出“设计模式”概念的是建筑设计大师亚力山大Alexander.在1970年他的<建筑的永恒之道>里描述了投计模式的发现,因为它已经存在了千百年之久,而现代才被通过大量的研究而被发现. 在<建筑的永恒之道>里这样描述:模式是一条由三个部分组成的通用规则:它表示了一个特定环境.一类问题和一个解决方案之间的关系.每一个模式描述了一个不断重复发生的问题,

[转]MySQL性能调优与架构设计&mdash;&mdash;第11章 常用存储引擎优化

第11章 常用存储引擎优化 前言: MySQL 提供的非常丰富的存储引擎种类供大家选择,有多种选择固然是好事,但是需要我们理解掌握的知识也会增加很多.每一种存储引擎都有各自的特长,也都存在一定的短处.如何将各种存储引擎在自己的应用环境中结合使用,扬长避短,也是一门不太简单的学问.本章选择最为常用的两种存储引擎进行针对性的优化建议,希望能够对读者朋友有一定的帮助. 11.1 MyI SAM存储引擎优化 我们知道,MyISAM存储引擎是MySQL最为古老的存储引擎之一,也是最为流行的存储引擎之一.对

【动力节点java培训】J2ee常用的设计模式

[动力节点java培训]J2ee常用的设计模式 Java中的23种设计模式: Factory(工厂模式),      Builder(建造模式),      Factory Method(工厂方法模式), Prototype(原始模型模式),Singleton(单例模式),    Facade(门面模式), Adapter(适配器模式),    Bridge(桥梁模式),       Composite(合成模式), Decorator(装饰模式),    Flyweight(享元模式),