热门标签 | 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 章:常用的设计模式


推荐阅读
  • 如何利用Java 5 Executor框架高效构建和管理线程池
    Java 5 引入了 Executor 框架,为开发人员提供了一种高效管理和构建线程池的方法。该框架通过将任务提交与任务执行分离,简化了多线程编程的复杂性。利用 Executor 框架,开发人员可以更灵活地控制线程的创建、分配和管理,从而提高服务器端应用的性能和响应能力。此外,该框架还提供了多种线程池实现,如固定线程池、缓存线程池和单线程池,以适应不同的应用场景和需求。 ... [详细]
  • 在探讨Hibernate框架的高级特性时,缓存机制和懒加载策略是提升数据操作效率的关键要素。缓存策略能够显著减少数据库访问次数,从而提高应用性能,特别是在处理频繁访问的数据时。Hibernate提供了多层次的缓存支持,包括一级缓存和二级缓存,以满足不同场景下的需求。懒加载策略则通过按需加载关联对象,进一步优化了资源利用和响应时间。本文将深入分析这些机制的实现原理及其最佳实践。 ... [详细]
  • 数字图书馆近期展出了一批精选的Linux经典著作,这些书籍虽然部分较为陈旧,但依然具有重要的参考价值。如需转载相关内容,请务必注明来源:小文论坛(http://www.xiaowenbbs.com)。 ... [详细]
  • 本文深入解析了Java面向对象编程的核心概念及其应用,重点探讨了面向对象的三大特性:封装、继承和多态。封装确保了数据的安全性和代码的可维护性;继承支持代码的重用和扩展;多态则增强了程序的灵活性和可扩展性。通过具体示例,文章详细阐述了这些特性在实际开发中的应用和优势。 ... [详细]
  • 单链表的高效遍历及性能优化策略
    本文探讨了单链表的高效遍历方法及其性能优化策略。在单链表的数据结构中,插入操作的时间复杂度为O(n),而遍历操作的时间复杂度为O(n^2)。通过在 `LinkList.h` 和 `main.cpp` 文件中对单链表进行封装,我们实现了创建和销毁功能的优化,提高了单链表的使用效率。此外,文章还介绍了几种常见的优化技术,如缓存节点指针和批量处理,以进一步提升遍历性能。 ... [详细]
  • CTF竞赛中文件上传技巧与安全绕过方法深入解析
    CTF竞赛中文件上传技巧与安全绕过方法深入解析 ... [详细]
  • 作为软件工程专业的学生,我深知课堂上教师讲解速度之快,很多时候需要课后自行消化和巩固。因此,撰写这篇Java Web开发入门教程,旨在帮助初学者更好地理解和掌握基础知识。通过详细记录学习过程,希望能为更多像我一样在基础方面还有待提升的学员提供有益的参考。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • 本文详细介绍了使用 Python 进行 MySQL 和 Redis 数据库操作的实战技巧。首先,针对 MySQL 数据库,通过 `pymysql` 模块展示了如何连接和操作数据库,包括建立连接、执行查询和更新等常见操作。接着,文章深入探讨了 Redis 的基本命令和高级功能,如键值存储、列表操作和事务处理。此外,还提供了多个实际案例,帮助读者更好地理解和应用这些技术。 ... [详细]
  • Java环境中Selenium Chrome驱动在大规模Web应用扩展时的性能限制分析 ... [详细]
  • C# .NET 4.1 版本大型信息化系统集成平台中的主从表事务处理标准示例
    在C# .NET 4.1版本的大型信息化系统集成平台中,本文详细介绍了主从表事务处理的标准示例。通过确保所有操作要么全部成功,要么全部失败,实现主表和关联子表的同步插入。主表插入时会返回当前生成的主键,该主键随后用于子表插入时的关联。以下是一个示例代码片段,展示了如何在一个数据库事务中同时添加角色和相关用户。 ... [详细]
  • DRF框架中Serializer反序列化验证机制详解:深入探讨Validators的应用与优化
    在DRF框架的反序列化验证机制中,除了基本的字段类型和长度校验外,还常常需要进行更为复杂的条件限制校验。通过引入`validators`模块,可以实现自定义校验逻辑,如唯一字段校验等。本文将详细探讨`validators`的使用方法及其优化策略,帮助开发者更好地理解和应用这一重要功能。 ... [详细]
  • 并发编程入门:初探多任务处理技术
    并发编程入门:探索多任务处理技术并发编程是指在单个处理器上高效地管理多个任务的执行过程。其核心在于通过合理分配和协调任务,提高系统的整体性能。主要应用场景包括:1) 将复杂任务分解为多个子任务,并分配给不同的线程,实现并行处理;2) 通过同步机制确保线程间协调一致,避免资源竞争和数据不一致问题。此外,理解并发编程还涉及锁机制、线程池和异步编程等关键技术。 ... [详细]
  • 在Android 4.4系统中,通过使用 `Intent` 对象并设置动作 `ACTION_GET_CONTENT` 或 `ACTION_OPEN_DOCUMENT`,可以从相册中选择图片并获取其路径。具体实现时,需要为 `Intent` 添加相应的类别,并处理返回的 Uri 以提取图片的文件路径。此方法适用于需要从用户相册中选择图片的应用场景,能够确保兼容性和用户体验。 ... [详细]
  • iOS 设备唯一标识获取的高效解决方案与实践
    在iOS 7中,苹果公司再次禁止了对MAC地址的访问,使得开发者无法直接获取设备的物理地址。为了在开发过程中实现设备的唯一标识,苹果推荐使用Keychain服务来存储和管理唯一的标识符。此外,还可以结合其他技术手段,如UUID和广告标识符(IDFA),以确保设备的唯一性和安全性。这些方法不仅能够满足应用的需求,还能保护用户的隐私。 ... [详细]
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社区 版权所有