软件设计原则——软件契约

软件契约

契约式设计把软件组件之间的交互描述成契约,权利与义务得到明确表达和强制实施。契约式
设计还没得到任何主流编程语言的原生支持。但是,有些框架可以让你在常用的语言里使用它,如
Java、Perl、Ruby、JavaScript,当然还有Microso什.NETFramework的语言。在.NET里,你通过.NET
Framework4新增的CodeContracts库创建软件契约。

在软件契约的实现里,不管语法细节如何,每当你写一个类的方法时,你应该确保你能回答以
下问题:
这个方法可以在哪些条件下调用?
方法结束之后哪些条件是经过验证的?
方法执行之前和之后哪些条件没有改变?
这3个条件也被分别成为前置条件、后置条件和不变条件。下面使用.№T的实现一一
Code
Contracts—作为例子深入探讨软件契约。

前置条件

前置条件是指必须验证才能执行方法的布尔条件。通常来说,前置条件会限制输入参数以及暴露该方法的类的当前状态。从概念上来说,前置条件与“如果一那么一抛出”模式一样。

public Macth(string id,string team1,string team2)
{
	Contract.Requires<ArgumentException>(id.IsAlphaNumeric(IdLength,IdLength));
	Contract.Requires<ArgumentException>(!team1.IsNullOrWhitespace());
	Contract.Requires<ArgumentException>(!team2.IsNullOrWhitespace());
}
public Match EndPeriod()
{
	Contract.Requires<ArgumentException>(IsInProcess());
	Contract.Requires<ArgumentException>(IsBallInPlay);
	Contract.Requires<ArgumentException>(!IsBallInPlay);
	IsBallInPlay = false;
	if(CurrentPeriod = LasePeriod)
		Finish();
	return this;
}

构造函数里的前置条件只检查输入数据,但如果你留意EndPeriod方法,你会发现前置条件也适用于对象的内部状态。如果比赛不在进行中,球也没有在打,你就不能结束这个场次。

后置条件

后置条件是指方法产生的输出和对象状态遭受的改变。
没有使用Code Contract

public Int32 Sum(Int32 ×,Int32 y)
{
	if(x == y)
	{
		var temp = x << 1;
		// 计算后置条件
		if(temp < 0)
			throw new ArgumentException();
	}
	
	var result = x + y;

	if(result < 0)
		throw new ArgumentException();
	
	return result;
}

使用了Code Contract

public Int32 Sum(Int32 ×,Int32 y)
{
	Contract.Ensures(Contract.Result<Int32>() >= 0);
	if(x == y)
		return x << 1;
	
	var result = x + y;
	return result;
}

Ensures方法捕获将要返回的值,确保它不是负数;如果是负数,这个方法会抛出ContractFailedException异常。

不变条件

不变条件涉及在任何公共方法(包括构造函数和写入属性)的执行期间都不会改变的类成员。不变条件可以用在所有需要避免对象在逻辑上处于不一致状态的场景里。

public class News
{
	public String Title{get;set;}
	public String Body{get;set;}
	[ContractInvariantMethodJ
	private void Objectlnvariant()
	{
		Contract.Invariant(!String.IsNuIIOrEmpty(TitIe));
		Contract.Invariant(!String.IsNuIIOrEmpty(Body));
	}
}

Invariant方法指定Title和Body都不能为空白字符串或null。把这个推广到领域类的任何业务规则,你就很容易保证你的对象总是处于有效状态了。如果某个使用News的代码把Title设为null,违反契约的异常马上就会抛出。更重要的是,你不必在调用任何公共方法之后写代码检查不变条件。

.NET Code Contracts的魔法

在.NET里,你使用一种高级语法来表达软件契约,这种语法基Contracts类的众多静态方法。但是,实际编译出来的代码有点不同。如果你使用反编译器(如dotPeek或.NETReflector)窥探Contracts类的代码,你会发现每个方法几乎都是void,而且只抛出异常。那么,窍门在哪里?

另一个集成到VisualStudio构建流程的工具(Code Contracts重写器)重塑了任何使用契约的代码,理解表达出来的前置条件和后置条件的目的,然后在它们逻辑上归属的地方把它们展开成相应的代码块。最终,重写器改变了编译器的输出,以“如果一那么一抛出”语句块的方式展开前置条件,把后置条件的“如果一那么一抛出”语句块移到每个检测到的退出点,用“如果一那么一抛出”包围不变条件放在每个公共方法的调用后面。所有这些工作对于你来说都是透明的,并且作为后置编译步骤来执行。

猜你喜欢

转载自blog.csdn.net/Star_Inori/article/details/83118503