我以前的痛苦,你也拥有吗?

我以前的痛苦,你也拥有吗?

记得笔者在以前使用Delphi/C++/Java开发软件时,经常需要撰写许多的输入验证码,例如当用户输入身份证号,或是输入数值时,都需要根据企业逻辑来检查用户输入的数据是否正确,通常许多的验证逻辑程序代码几乎都是一样的,但是对于不同的程序语言,不同的开发环境都可能需要不断重写。更麻烦的是,由于一个团队拥有许多的开发人员,因此不同的开发人员可能会使用不同的程序代码,不同的验证逻辑,并且分散在整个应用程序不同的地方进行数据验证的工作。这造成了许多的困扰,例如当验证逻辑需要修改或是更正时,可能忘记改某些地方,或是由于开发人员撰写的方式不同,而让一些臭虫因此而出现。虽然后来许多的开发人员把这些验证程序代码封装成函式库统一让开发人员使用,或是封装成组件让开发人员使用,例如现在.NET/Java都提供了一些简单,常用的Validation组件,但是验证程序代码分散在整个应用程序不同的地方的问题仍然无法得到解决,而由这个现象延伸出的问题也一样存在。那么我们有什么好方法可以解决吗?

在回答之前也许我们应该想想这些验证程序代码的目的到底是什么?在这些验证程序代码中除了一般检查用户输入的字符,格式,长度或是语意之外,最重要的验证工作应该是用户的输入应该符合应用程序的企业逻辑规则,不是吗?

因此如果我们能够把这些验证程序代码进行的工作定义在企业逻辑模型之中,那么不管日后开发人员使用什么程序语言,或是在应用程序的什么地方,开发人员只需要在需要进行验证程序代码的工作时从企业逻辑模型中最出定义好的验证逻辑,再根据这些验证逻辑来验证用户输入的数据。如此一来一旦验证逻辑有改变或是更新,我们只需要更新定义在企业逻辑模型之中的验证逻辑,那么由于整个应用程序都是从企业逻辑模型中取得验证逻辑,那么我们不就解决了可能忘记更改一些分散在不同地方的问题了吗? 更进一步的想想,如果我们能够使用一段通用的程序代码来检查企业逻辑规则,而不需要对每一个不同的用户输入撰写不同的程序代码来检查,那么不是更棒吗(如果读者真的开发过这样的应用程序验证逻辑,就知道笔者说的痛苦,以及难以维护的程序代码了)?

问题是,能够有方法解决这长久以来的痛苦吗?

也许有可能,让我们继续往下看。

UML中,开发人员可以使用OCL来定义类别/属性的验证逻辑或是约束条件,之后开发人员便可以在程序代码中藉由EcoSpace取得这些约束条件(Constraints),并且使用来验证异动的对象是否符合这些约束条件,一旦符合约束条件才能够更新回数据来源之中。在ECO架框中支持了这样的机制,ECO藉由提供开发人员使用OCLECO类别/ECO属性定义约束条件,以及在ECO架框中提供服务让开发人员能够在程序代码中存取这些约束条件并且执行约束条件以便验证对象。

在您了解了上面讨论的观念之后,接下来的内容中将深入的说明如何在企业逻辑模型中使用OCL来定义约束条件,并且使用来验证对象。

在企业逻辑模型中定义约束条件

开发人员可以在ECO的类别设计家中藉由对象检视器设定类别或是属性的Constraints特性来定义约束条件。使用Constraints特性定义约束条件时开发人员必须了解下面的观念:

l Constraints特性可以定义任何数目的约束条件

l 约束条件是使用OCL来撰写的

l 开发人员使用Constraints特性定义约束条件后,如果用户违反了约束条件那么仍然可以更新对象回数据来源。因此检查对象是否违反约束条件是开发人员的责任,开发人员必须确定对象在更新回数据来源之前没有违反约束条件。

现在让我们以图1的范例模型来说明如何定义约束条件以及如何在程序代码中检查企业逻辑模型的约束条件。

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><span lang="EN-US" style="FONT-SIZE: 9.5pt; COLOR: #404040; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-fareast-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><span lang="EN-US" style="FONT-SIZE: 9.5pt; COLOR: #404040; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-fareast-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><img height="353" alt="" width="409" src="http://tkfiles.storage.msn.com/x1pFUxp6NG29bXY4hHUvhWU938vgNEsQW76lC8_5YICKBfHq8uRwTIDmA4mifa3QmX5Vqk3d9LBD5AmkfEdvlQ_vmycFoI-WZO0VTa3h81aJrwTsAr0b04asOfa0lICxuJzq7gRaHlVHAbPky3m3lJl1g"><span lang="EN-US" style="FONT-SIZE: 9.5pt; COLOR: #404040; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-fareast-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"></shapetype></span></shapetype></span></shapetype></span></shapetype>

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 306pt; HEIGHT: 264pt" alt="" type="#_x0000_t75"><imagedata o:href="http://tkfiles.storage.msn.com/x1pFUxp6NG29bXY4hHUvhWU938vgNEsQW76lC8_5YICKBfHq8uRwTIDmA4mifa3QmX5Vqk3d9LBD5AmkfEdvlQ_vmycFoI-WZO0VTa3h81aJrwTsAr0b04asOfa0lICxuJzq7gRaHlVHAbPky3m3lJl1g" src="file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.jpg"></imagedata></shape>

1 范例模型

如下图所示,我们可以点选Joiner类别,并且在对象检视器中定义Constraints特性:

<shape id="_x0000_i1026" style="WIDTH: 264pt; HEIGHT: 210pt" alt="" type="#_x0000_t75"><imagedata o:href="http://tkfiles.storage.msn.com/x1pFUxp6NG29bXY4hHUvhWU938vgNEsQW76lC8_5YICKBcVmcpmE8G1ocSXxCrdxOIgM10ReqId894DdhrbXRye-wi_QEugjuxvERz3VmOt-VstItlYOqdkOq63qQbf3h5a-EPwHkZLkL54SONjr0wBoQ" src="file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image003.jpg"></imagedata></shape>

2 ECO设计家中为类别/属性定义约束条件

开发人员可以直接在Constraints特性中输入OCL或是点选Constraints特性旁的按钮启动Constraints编辑器,点选Constraints编辑器下方的Add按钮加入约束条件。下面的图形加入了两个约束条件,每一个约束条件必须输入约束条件的名称以及约束条件的OCL叙述,例如NameNotEmpty约束条件规定了Name属性不可为空白,同样的EMailNotEmpty也是类似的规定。

<shape id="_x0000_i1027" style="WIDTH: 356.25pt; HEIGHT: 83.25pt" alt="" type="#_x0000_t75"><imagedata o:href="http://tkfiles.storage.msn.com/x1phiCZJPhsUYg-Rf0H_plhOdSXMk_dnDiM8qsV6ywc6F3uYxTRe7HMJ8Sh9G06WJyiQC2iW1f7Lwwv3MtUUyP8Y01CFaHJfxWzdFWSM5APnoPRBgsJn4sMBA6PBcmK3W9T6T2YSwtuUb8" src="file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image005.jpg"></imagedata></shape>

<shape id="_x0000_i1028" style="WIDTH: 414.75pt; HEIGHT: 73.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://tkfiles.storage.msn.com/x1phiCZJPhsUYg-Rf0H_plhOXYx_TAvJCyDG5gK2PJO_jGTuy7xyW7IXHSyhIqqH2I0VrFXdSzW1ZDRqlwtZRjjvTnNtl0MWzzozJbepZamh8SB_tIgaJ6I8XUeGfwaa32U5SDJI4-IeSo" src="file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image007.jpg"></imagedata></shape>

3 启动约束条件编辑器为Joiner类别定义约束条件

最后,范例企业逻辑模型也为DevCoSeminarMaxCount属性定义了最多参加人数的约束条件必须小于120:

<shape id="_x0000_i1029" style="WIDTH: 381pt; HEIGHT: 79.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://tkfiles.storage.msn.com/x1phiCZJPhsUYg-Rf0H_plhOZTkr49tqFLWT4TwQIYW5p8UBpqQ_l0vTML9e19L2TZWnu2OuMMDSitaiJ51KAx-veP9Nz4UwsU5WfpnMusLVdMAmglbr7-UDqY3NWC3B_VWGht4Qcoz_NI" src="file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image009.jpg"></imagedata></shape>

4 启动约束条件编辑器为DevCoSeminar类别定义约束条件

定义完Constraints的约束条件后,让我们再解释一下封装约束条件的IConstraint界面。

IConstraint界面

ECO架框中企业逻辑模型中的约束条件是由IConstraint接口封装,定义的,开发人员藉由ECO架框服务接口存取到IConstraint接口,再藉由执行IConstraint接口定义的约束条件来验证对象是否符合企业逻辑。下面是IConstraint接口的定义:

public interface IConstraint: IModelElement

{

IExpression Body { get; }

IEcoConstraint EcoConstraint { get; }

}

IConstraint接口有两个只读属性,其中的Body即代表封装的约束条件,而EcoConstraint则是专属于ECO的额外信息。Body属性型态是IExpression接口,而IExpression接口的定义如下:

public interface IExpression

{

string Language { get; }

string Body { get; }

}

IExpression接口也有两个只读属性,其中的Language属性会回传约束条件使用的语言,通常都是”OCL”,代表是由OCL撰写的约束条件。而Body属性则是约束条件本身。例如前面的“NameNotEmpty”约束条件,它的IConstraint.Body.Body属性便是self.Name.Length>0

IEcoConstraint接口也定义了两个只读属性,IsAutoGenerated属性回传这个约束条件是否是自动产生的,而Description则是约束条件的叙述文字:

public interface IEcoConstraint

{

bool IsAutoGenerated { get; }

string Description { get; }

}

因此在ECO程序代码中,要根据约束条件来验证对象,它的执行步骤如下:

l ECO企业逻辑对象中取得所有的约束条件的IConstraint接口对象

l 一一的使用IOclService执行这些约束条件并且判断约束条件是否符合

l 如果ECO企业逻辑对象符合约束条件才能够更新回数据来源

l 如果ECO企业逻辑对象不符合约束条件,那么就通知用户进行后续的处理

了解了IConstraint界面以及处理约束条件的步骤之后,接下来我们就可以使用范例和程序代码来展示如何使用约束条件了。

在程序代码中验证对象的约束条件

要如何使用程序代码来检查定义在企业逻辑模型中的约束条件呢? 其实非常的简单的,本书在第4章已经介绍了ECO的企业逻辑模型的静态通用机制,因此我们可以使用下面的步骤来完成检查企业逻辑模型中的约束条件:

l 在更新异动对象回数据来源之前,藉由使用IDirtyListService服务界面取得用户所有异动的对象

l 藉由ECO的企业逻辑模型的静态通用机制取得定义在企业逻辑模型中的约束条件

l 藉由IOclService服务接口一一的执行约束条件并且检查是否违反约束条件

l 如果没有违反任何的约束条件就更新对象回数据来源中

l 如果违反约束条件的话,保留违反约束条件的对象,要求用户进一步的处理

了解了进行约束条件检查的步骤之后,让我们看一些实际的程序代码来验证上面的步骤。

在下面的片段程序代码中是准备呼叫EcoSpaceUpdateDatabase方法把用户异动的对象更新回数据来源中,但是在这之前,它先呼叫了DoCheckObjectConstraints方法以便确定所有异动的对象是符合企业逻辑模型中的约束条件,否则DoCheckObjectConstraints会产生一个例外错误。

001 procedure TWinForm.Button2_Click1(sender: System.Object; e: System.EventArgs);

002 begin

003 try

004 DoCheckObjectConstraints;

005 EcoSpace.UpdateDatabase;

006 except on E: Exception do

007 MessageBox.Show ('对象违反约束条件' + e.Message +'无法更新回数据库',

008 MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk);

009 end;

end;

DoCheckObjectConstraints方法中先在007行取得IDirtyListService接口,接着进入010行的for循环一一的取出每一个异动的对象并且呼叫DoHandleDirtyObject来检查每一个异动对象的约束条件是否符合。

001 procedure TWinForm.DoCheckObjectConstraints;

002 var

003 dol : IDirtyListService;

004 Iobj : IObject;

005 begin

//藉由IdirtyListServices取得异动的对象

007 dol := EcoSpace.DirtyListService;

008 if (dol.HasDirtyObjects) then

009 begin

010 for Iobj in dol.AllDirtyObjects do

011 begin

012 DoHandleDirtyObject(Iobj);

013 end;

014 end;

015 end;

最后的DoHandleDirtyObject方法在008行取得IOclService接口,在009行进入for循环,藉由IObject界面的UmlType.Constraints取得定义在这个对象类别中的所有约束条件,接着在012行呼叫IOclServiceEvaluate方法来执行/评量约束条件。如果有任何对象违反了任何的约束条件就产生一个例外错误。

001 procedure TWinForm.DoHandleDirtyObject(Iobj : IObject);

002 var

003 IOcl : IOclService;

004 ICnt : IConstraint;

005 iCount: Integer;

006 bResult : boolean;

007 begin

008 IOcl := EcoSpace.OclService;

009 for iCount := 0 to Iobj.UmlType.Constraints.Count - 1 do

010 begin

011 ICnt := Iobj.UmlType.Constraints.Item[iCount];

012 bResult := boolean(IOcl.Evaluate(Iobj as IElement,iCnt.Body.Body).AsObject);

013 if (not bResult) then

014 raise Exception.Create(ICnt.Name);

015 end;

016 end;

现在如果我们执行上面的范例程序代码并且搭配图3和图4定义的约束条件,那么我们可以看到当执行下面的范例程序并且在DevCoSeminar对象的MaxCount属性中输入超过120的数值:

<shape id="_x0000_i1038" style="WIDTH: 272.25pt; HEIGHT: 221.25pt" alt="" type="#_x0000_t75"><imagedata o:href="http://tkfiles.storage.msn.com/x1phiCZJPhsUYg-Rf0H_plhOSWtknxeiS2-QkdBqtyffwpJUWyAhXyFVz2eNUVx8s3g24KpB9nnE29cftJOC3JLjoIC3cRg4nV9OOTiBKiDmwr2T01m_PEr6-Q-4swXsM6Gnq3yJFuPrEc" src="file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image011.jpg"></imagedata></shape>

5 修改DevCoSeminar对象的MaxCount属性值

那么在更新对象回数据来源之前就会看到下面的例外错误,代表上面的程序代码果然检查出了对象违反了约束条件。

<shape id="_x0000_i1039" style="WIDTH: 242.25pt; HEIGHT: 81.75pt" alt="" type="#_x0000_t75"><imagedata o:href="http://tkfiles.storage.msn.com/x1phiCZJPhsUYg-Rf0H_plhOWCKcfJtNVpJLY0ypd5yudKcNWSZxcPMjpGybfXzE2jNT2AwvHcgYOAe0SJd8xpumQO8swuWg8wa_6xlK8NI-I2vzdjTfLQodMwWhbwXDzagLvAwc964i1s" src="file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image013.jpg"></imagedata></shape>

6 程序代码检查出用户输入的数据违反了约束条件

如果我们接着又如下图对Joiner类别进行异动并且试着把EMail属性清为空白:

<shape id="_x0000_i1040" style="WIDTH: 296.25pt; HEIGHT: 222pt" alt="" type="#_x0000_t75"><imagedata o:href="http://tkfiles.storage.msn.com/x1phiCZJPhsUYg-Rf0H_plhOVxd_GP_XaUl1EruoaxcjOksXwxEk7f0J3KbAJVIhxfFtPOKzW3_gr1uNieA7Ip7BT7fbq8W1Zc8qvyu48h0GTfiNAJMiBruxb0vDT9UpVvg-ltczns48mo" src="file:///C:%5CDOCUME~1%5CADMINI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image015.jpg"></imagedata></shape>

7 程序代码检查出用户输入的数据违反了约束条件

那么在更新对象回数据来源之前我们又可以看到范例程序检查出Joiner对象违反了EmailNotEmpty的约束条件。

从上面的程序代码看到了什么? 如果您有注意的话会发现只需要使用相同的一份程序代码,在一个程序/函式中就可以检查任何对象的任何约束条件。这和以前许多开发人员需要使用不同的程序代码,在不同的地方检查不同的用户输入资料是完全不一样的,这可以让程序代码更容易维护,也不容易产生臭虫,或是因为疏失而造成的错误。

如果我们进一步的结合.NET/开发工具提供的Validation组件和约束条件,那么我相信大多数的程序代码验证工作都可以顺利,有效率的完成。

您注意到,感觉到了使用模型和约束条件的好处了吗?

阅读更多
换一批

没有更多推荐了,返回首页