热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

第9章:常用的设计模式9.1聚合组件考虑为常用的特性域提供聚合组件。要用聚合组件来对高层的概念(物理对象)进行建模,而不是对系统级的任务进行建模。要让聚合组件的名字与众所周知的系统

第 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。

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

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

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

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

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

  要为每个异步操作定义单独的 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,其目的是为了得到基本的 LINQ 支持。

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

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

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

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

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

  要让实现了 Query 模式在类型实现了 IEnumerable

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

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

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

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

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

  要在参数中使用 Expression>,而不是 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 语法。

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


推荐阅读
  • 深入剖析JVM垃圾回收机制
    本文详细探讨了Java虚拟机(JVM)中的垃圾回收机制,包括其意义、对象判定方法、引用类型、常见垃圾收集算法以及各种垃圾收集器的特点和工作原理。通过理解这些内容,开发人员可以更好地优化内存管理和程序性能。 ... [详细]
  • 深入解析动态代理模式:23种设计模式之三
    在设计模式中,动态代理模式是应用最为广泛的一种代理模式。它允许我们在运行时动态创建代理对象,并在调用方法时进行增强处理。本文将详细介绍动态代理的实现机制及其应用场景。 ... [详细]
  • ListView简单使用
    先上效果:主要实现了Listview的绑定和点击事件。项目资源结构如下:先创建一个动物类,用来装载数据:Animal类如下:packagecom.example.simplelis ... [详细]
  • 本文详细介绍了get和set方法的作用及其在编程中的实现方式,同时探讨了点语法的使用场景。通过具体示例,解释了属性声明与合成存取方法的概念,并补充了相关操作的最佳实践。 ... [详细]
  • 探讨 HDU 1536 题目,即 S-Nim 游戏的博弈策略。通过 SG 函数分析游戏胜负的关键,并介绍如何编程实现解决方案。 ... [详细]
  • 本题要求在一组数中反复取出两个数相加,并将结果放回数组中,最终求出最小的总加法代价。这是一个经典的哈夫曼编码问题,利用贪心算法可以有效地解决。 ... [详细]
  • 本文探讨了C++编程中理解代码执行期间复杂度的挑战,特别是编译器在程序运行时生成额外指令以确保对象构造、内存管理、类型转换及临时对象创建的安全性。 ... [详细]
  • 在高并发需求的C++项目中,我们最初选择了JsonCpp进行JSON解析和序列化。然而,在处理大数据量时,JsonCpp频繁抛出异常,尤其是在多线程环境下问题更为突出。通过分析发现,旧版本的JsonCpp存在多线程安全性和性能瓶颈。经过评估,我们最终选择了RapidJSON作为替代方案,并实现了显著的性能提升。 ... [详细]
  • 本文介绍了如何在 C# 和 XNA 框架中实现一个自定义的 3x3 矩阵类(MMatrix33),旨在深入理解矩阵运算及其应用场景。该类参考了 AS3 Starling 和其他相关资源,以确保算法的准确性和高效性。 ... [详细]
  • 在尝试使用C# Windows Forms客户端通过SignalR连接到ASP.NET服务器时,遇到了内部服务器错误(500)。本文将详细探讨问题的原因及解决方案。 ... [详细]
  • This post discusses an issue encountered while using the @name annotation in documentation generation, specifically regarding nested class processing and unexpected output. ... [详细]
  • Linux环境下进程间通信:深入解析信号机制
    本文详细探讨了Linux系统中信号的生命周期,从信号生成到处理函数执行完毕的全过程,并介绍了信号编程中的注意事项和常见应用实例。通过分析信号在进程中的注册、注销及处理过程,帮助读者理解如何高效利用信号进行进程间通信。 ... [详细]
  • Spring Boot 中静态资源映射详解
    本文深入探讨了 Spring Boot 如何简化 Web 应用中的静态资源管理,包括默认的静态资源映射规则、WebJars 的使用以及静态首页的处理方法。通过本文,您将了解如何高效地管理和引用静态资源。 ... [详细]
  • 编写css让div2在div1的右下角? ... [详细]
  • 本文介绍了如何通过在数据库表中增加一个字段来记录文章的访问次数,并提供了一个示例方法用于更新该字段值。 ... [详细]
author-avatar
love留着对她说吧
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有