zoukankan      html  css  js  c++  java
  • 扩展Kevin McFarlane的C#版DesignByContract Framework

    Kevin McFarlane的C#版DesignByContract Framework实现从02年在CodeProject发布至今,几乎成为C#开发中大多数朋友使用的事实标准。本文结合对该框架的使用经验,在Kevin的原始版本的基础上,使用Strategy Pattern对其进行进一步的扩展,对最常用的检查语义进行封装简化。本文改进的源码以Public Domain协议发布,也就是说,完全没有任何限制。

    如果您愿意,不强制,使用该代码时,请保留该源码文件顶部的注释。

    Kevin McFarlane的C#版DesignByContract Framework实现在CodeProject.com的原文地址:http://www.codeproject.com/csharp/designbycontract.asp

    下载改进版本的DesignByContract源码及测试工程源码(测试工程为VS2005 VSTS测试工程格式):DesignByContract_Enhanced_Version.zip

    使用简介

    对于该框架的基本使用介绍,请参见上面Kevin McFarlane写在CodeProject.com的原文

    下面主要介绍扩展部分的使用方法。

    启用Trace或Debug的Assertion

    类似原始版本,默认情况下Check.Require()等方法失败会抛出PreconditionException等异常,可以在它的工程的Conditional compilation symbols中添加USE_TRACE_ASSERTIONUSE_DEBUG_ASSERTION。这两个symbols分别表示用System.Diagnostics.Trace.Assert()或System.Diagnostics.Debug.Assert()提示检验失败消息,而不是默认的抛出异常。

    当使用这两个symbols时,可以在使用代码的启动初始化代码(例如Global.asax的Application_Start)中通过类似下面的代码改变默认的错误提示或日志记录方式:

    System.Diagnostics.Trace.Listeners.Clear();
    System.Diagnostics.Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));

    注1:对ASP.NET程序,请总是使用USE_DEBUG_ASSERTION。
    注2:默认情况下,.NET 2.0程序工程的trace和debug在工程Debug编译模式下是默认启用的,Release编译模式下trace默认被启用,debug默认并不启用,不过,可以在工程的属性中手动修改这两个设置。

    修改错误提示消息文本模版

    原始版本中的默认错误提示是English的,而且直接hardcode,并且分散在源代码各个方法中,如果我们想改变错误提示类型就不是那么方便,在本改进版本中,所有的错误提示消息的文本模版我都提出来放到源文件的Check类定义最开头的#region Const Literals中,方便大家修改。如果您需要从外部资源文件中读取这些信息,您可以将#region Const Literals中的这些变量都改成private static readonly的,并在Check的static构造函数中从外部资源文件读取。

    基于Strategy模式的Check逻辑封装和使用

    在改进版本中最大的改进在于增加了一组Check Strategies。所有的Check Strategy都实现ICheckStrategy接口,并在Check类中定义了静态成员变量来方便访问他们的实例。

    ICheckStrategy的定义如下:

    1        public interface ICheckStrategy
    2        {
    3            bool Pass(object obj);
    4            string GetFailingMessage(string objName);
    5        }

    Pass方法需要实现对obj的检查逻辑,返回true表示通过检查,GetFailingMessage方法用于返回错误提示消息。

    本改进版本内置实现了如下的Check Strateies:

    NotNull - 可用于检查任意对象是否为null
    NotNullOrEmpty - 可用于检查string,Array或ICollection类型的对象非null且非空,Array和ICollection为空这里指其包含的元素个数为0
    IsAssignableTo<TargetType> - 可用户检查指定对象是否可以被转换为TargetType类型的实例
    GreaterThan<T>/LessThan<T>/GreaterThanOrEqual<T>/LessThanOrEqual<T> - 顾名思义,这一组Strategies可用于检查大于、小于等范围

    这些类并不需要也不能(内置实现类的构造函数是private的,不能直接调用)显式实例化,只需要使用Check类的静态成员进行访问:

    如:

    Check.NotNull
    Check.NotNullOrEmpty
    Check.IsAssignableTo<TargetType>()
    Check.GreaterThan<T>(T compareValue)
    Check.LessThan<T>(T compareValue)
    Check.GreaterThanOrEqual<T>(T compareValue)
    Check.LessThanOrEqual<T>(T compareValue)

    同时,本改进版本也为Check的Require,Ensure等方法增加了一个象下面这样的重载:

    public static void Require(object obj, string objName, params ICheckStrategy[] strategies)

    该重载的第一个参数为要检查的对象,第二个参数表述被检查对象的描述名称(不一定是变量或参数名,可以使更易于理解的描述名称),第三个参数是一组可变数量的ICheckStategy实例。

    使用示例(更多使用示例可以参见本文源代码中的测试工程):

    1        private static void RequireStrategy(TestClass obj, object objs, object objList)
    2        {
    3            Check.Require(obj, "obj", Check.NotNull);
    4            Check.Require(objs, "objs", Check.NotNull, Check.IsAssignableTo<Array>(), Check.NotNullOrEmpty);
    5            Check.Require(objList, "objList", Check.NotNull, Check.IsAssignableTo<ICollection>(), Check.NotNullOrEmpty);
    6        }

    示例代码中的RequireStategy方法的被检查参数类型可以但不一定是强类型的。

    对于最常用的NotNull检测,如Check.Require(obj, "obj", Check.NotNull)中,Check.NotNull可以不指定,也就是说Check.Require(obj, "obj")等价于显式指定Check.NotNull。

    Check.Require()的这个重载的最后一组参数是一组Check Stategies,当指定多个时,他们的Pass方法会被依次调用,一旦遇到Pass返回false,则终止后续检查,提示信息只包含第一个Pass通不过的Stategy的错误提示。

    以上示例代码中的第二、三个Require()方法的调用,相当于检查数组或集合对象的类型和是否非空(包含的元素数为0)。NotNullOrEmpty这个Strategy也可以用于对string类型的检查。对非string、Array或ICollection类型,他的Pass方法总是返回false的。

    类似的,使用GreaterThan等Strategies的示例代码如下:

    1        private static void CompareArguments(int x, int y, double z)
    2        {
    3            Check.Require(x, "x", Check.GreaterThan<int>(0));
    4            Check.Require(y, "y", Check.LessThanOrEqual<int>(0));
    5            Check.Require(z, "z", Check.GreaterThanOrEqual<double>(0.1));
    6        }

    我们也可以用像Check.Require(x, "x", Check.GreaterThan<int>(0), Check.LessThanOrEqual<int>(10))这样的语法检查x是否0<x<=10。

    您可能要问使用Strategy这样的语法相比Check.Require(obj != null, "obj could not be null.")这样的语法有何优势?

    最大的优势在于,您不必对常用的检查类型一遍遍重复的指定错误的描述信息,如"obj could not be null."这种描述信息,可以少打很多字母。而且,基于Strategy使得,对常用检查逻辑的封装扩展非常灵活便捷。我们可以方便的继承ICheckStrategy接口,扩展我们自己的查询逻辑。

    前面说过了,在Check类的#region Const Literals中定义了所有的默认错误描述消息模版,我们可以根据需要修改默认的提示消息,当然,对大多数应用来说,使用默认的English提示消息也足够了。下面简单列举了在默认抛出异常工作模式下一些常见的Strategy检查失败后的错误消息:

    Check.NotNull - Test method DesignByContract.UnitTests.TestPreconditionNull threw exception:  DesignByContract.PreconditionException: obj could not be null.

    Check.NotNullOrEmpty - Test method DesignByContract.UnitTests.TestPreconditionStrategyCollectionEmpty threw exception:  DesignByContract.PreconditionException: objList could not be null or empty.

    Check.IsAssignableTo<ICollection>() - Test method DesignByContract.UnitTests.TestPreconditionStrategyCollectionNotIsAssignableFrom threw exception:  DesignByContract.PreconditionException: objList is not assignable to System.Collections.ICollection.

    Check.GreaterThanOrEqual<double>(0.1) - Test method DesignByContract.UnitTests.TestCompareDoubleGreaterThanOrEqual threw exception:  DesignByContract.PreconditionException: obj must be >= 0.1.

    Last Updated: 2007/10/5

    //The End
  • 相关阅读:
    记录「十一月做题记录」
    题解「GMOJ6898 【2020.11.27提高组模拟】第二题」
    题解「CFGYM102331B Bitwise Xor」
    题解「Japan Alumni Group Summer Camp 2018 Day 2J AB Sort」
    题解「AGC048B Bracket Score」
    题解「中位数之中位数 median」
    记录「十月做题记录」
    测试「2020牛客NOIP赛前集训营-提高组(第五场)」
    测试「20201028测试总结」
    定时提醒助手
  • 原文地址:https://www.cnblogs.com/teddyma/p/914656.html
Copyright © 2011-2022 走看看