Visual Studio 2010层架构验证的实现
<p>我们将使用一段非常简单的代码,主要强调的是代码所代表的概念,而不是代码的细节。并将在现有代码层关系架构逻辑设计分析的基础上进行层验证(Layer Validation)功能:</p><p>① 打开Visual Studio 2010,通过Modeling Projects模板指定解决方案(Solution)的名称为LayerValidation(File|New|Project命令,选择 ModelingProject,命名为LayerValidation),并提供创建一个名为“Client”的C#控制台程序,单击“OK”按钮。</p><p>②在解决方案浏览器中,使用鼠标右键单击Solution节点,选择“New Project…”命令,在弹出的对话框中选择“Class Library”并将工程命名为“Implementation”。</p><div style="page-break-after: always;"><span style="display: none;"><!--more-->& nbsp ;</span></div><p>③ 重复以上几步,创建名为“Interfaces”和“Creators”的Class Library工程。展开Interfaces工程节点,用鼠标右键单击Class1.cs,选择“Rename”命令,将该文件重命名为 “IDataRetriever.cs”,并在弹出的对话框中选择“Yes”。文档编辑窗口和Solution Explorer如图5所示。</p><p> </p><p>图5 文档编辑窗口和Solution Explorer</p><p>④ 把class的关键字改为interface,将IDataRetriever变成一个接口。为IDataRetriever添加一个get属 性,该属性返回一个IData类型的对象。在IData下面有红色波浪线,表示IData不存在。Visual Studio 2010的新功能可以自动解决这个问题:用鼠标右键单击出错的IData,选择“Generate”命令,然后选择“Other…”命令,将看到一个 “New Type”对话框。将其中的“Access:”修改为“public”,将“Kind:”修改为“interface”,其他的保留默认设置,单击 “OK”按钮。VS会自动向Interfaces工程添加一个IData.cs文件,并在文件中创建一个名为IData的接口。</p><p>⑤展开Implementation工程节点,用鼠标右键单击References节点,选择“Add Reference…”命令,在弹出的对话框中选择Projects页,然后选择Interfaces工程,单击“OK”按钮。</p><p>⑥ 将Class1.cs重命名为DataRetriever.cs。打开DataRetriever.cs文件,修改DataRetriever类 使其实现IDataRetriever接口。当输入IDataRetriever的时候没有出现智能输入支持,可以手动输入 IDataRetriever,然后会发现IDataRetriever下面又出现了红色波浪线。将鼠标移动到IDataRetriever上,会注意到 在这个单词开始的位置下方有一个方形的小图标。单击它并选择“using Interfaces;”命令,它会自动为你添加所需的using语句,如图6所示。 </p><p></p><p>图6 自动化提示添加代码语句(名字空间)</p><p>现 在“using Interfaces”已经自动添加好了。再次选中这个图标,不过这次选择“Implement interface ‘IDataRetriever’”命令,可自动生成“DataRetriever”代码文件。如上所示,创建了一个对象,调用了对象的一个属性,然后抛 出一个“NotImplementedException”异常,程序描述了一个实际系统中经常遇到的问题。</p><p>⑧接下来向Client工程中添加到Implementation和Interfaces工程的引用。打开Client工程中的Program.cs文件,参考代码如下:</p><pre> </pre><ol> <li>using System; </li> <li>using System.Collections.Generic; </li> <li>using System.Linq; </li> <li>using System.Text; </li> <li>using Implementation; </li> <li>using Interfaces; </li> <li> </li> <li>namespace Client </li> <li>{ </li> <li>class Program </li> <li> { </li> <li>static void Main(string[] args) </li> <li> { </li> <li>DataRetriever dr = new DataRetriever(); </li> <li>IData data = dr.Data; </li> <li> } </li> <li> } </li> <li>} </li></ol><p>在 这段代码中,Client工程直接访问了一个接口(IDataRetriever)的实例(DataRetriever)。在没有需求功能扩展前 没有太大问题,因为所有的数据是从DataRetriever中获取(可以想象DataRetriever是从SQL数据库中获取的数据)。如果将来需要 从另一种数据源中获取数据,在不改动应用程序其他部分的情况下实现需求,可以使用Layer Diagram和Layer Validation来保证开发代码不会违反这一设计。</p><p>我们可以不对接口的具体实现做任何设置,而仅仅依赖于接口本身。这是一个相当普遍的设计模式,但是在现实应用中很容易被违反。只要一行错误的代码就会破坏这个模式,从而在建立模块间出现了不必要的依赖关系(通常使用控制反转(IoC)来解决这个问题)。</p><p>⑨ 创建Layer Diagram。可以创建一个Layer Diagram来可视化地描述在架构中想要维护的约束关系。单击主菜单的Architecture|New Diagram命令,选择“Layer Diagram”命令,并将层图命名为“FirstLayerDiagram.layerdiagram”,在弹出的对话框中,将工程命名为 “FirstModelingProject”。</p><p>⑩塑模范本,将代码映射到层上。在Layer Diagram Designer中,从工具箱中拖曳出三个Layer工具到设计平面上,分别由上至下指定层的名称为Client、Interface、 Implementation,代表应用程序、工作接口和方法。表示的是Client Logic层依赖于Interfaces层,Implementation层同样依赖于Interfaces层。但是Client Logic层和Implementation层之间没有依赖关系,如图7所示。</p><p></p><p>图7 创建映射</p><p>如 上图所示,然后建立各个层次之间的相互关系。从工具箱中选择Dependency工具,在Solution Explorer中,选中Client工程并将它拖曳到Layer Diagram上的Client Logic层上,代表Client层会依赖Interface层。这时出现了一个由Client指向Interface的箭头链接。将 Interfaces工程拖到Interfaces层上;最后,将Implementation工程拖到Implementation层上,代表 Implementation层会依赖Interface层;在层右上角的数字“1”表示该层已经和一个工程相关联。</p><p>如果选中Client Logic、Interfaces和Implementation层,再打开Layer Explorer,就可以看到和当前层关联的项目,这里是Client.exe、Interfaces.dll和Implementation.dll, 然后就可以用这张图来对代码进行约束与验证,如图8所示。 </p><p></p><p>图8 进行架构验证</p><p>如上图所示,进行架构验证。用鼠标右键单击Layer Diagram的任何位置,选择“Validate Architecture”命令,进行验证。</p><p>验证架构(Validate Architecture):可以检查出我们的程序是否破坏了层次图中的依赖关系,如果我们的程序中的CaryLayer项目中的程序调用了Common项目中的类等于就违反了以前设计好的层次图,在验证架构的时候就会失败。</p><p>依赖关系(Generate Dependencies):可以根据我们程序中的调用关系生成层的依赖关系。</p><p>错 误列表。命令执行完成后会看到“Error List”窗口中有三条错误信息,同时指示错误发生的区域。检视一下错误内容,会发现我们要求的层次依赖关系被破坏了。这是因为Client工程中的 Program.cs直接使用了Implementation工程中定义的类型。而在刚才创建的图中,这种依赖关系是错误的,如图3错误列表提示。</p><p>修正代码,解决错误问题。</p><p>打开Program.cs文件,需要确保只使用Interfaces工程中定义的类型,而不能直接使用Implementation工程中定义的类型。我们需要在不产生直接依赖关系的情况下创建实现IDataRetriever接口的对象。</p><p>解 决方法是使用Factory模式,利用Factory建立以接口为主的方法。当以后要传回的信息接收器是针对不同的信息来源进行处理的,只要调整 Factory方法传回对应的接收器即可,原本的应用程序不用改动,因为它都是通过接口决定作业的,只要实做了同样接口的类都可以套用,从而增加了程序的 弹性和维护能力。使用工厂(Factory)模式来解决这个问题的步骤如下:</p><p>在Solution Explorer中展开Creators工程,将Class1.cs重命名为TypeCreator.cs。</p><p>向Creators工程中添加对Implementation和“Interfaces”工程的引用(Creators工程现在依赖于Implementation和Interfaces工程)。</p><p>打开TypeCreators.cs,向其中添加一个静态方法,该方法返回一个IDataRetriever的对象。</p><p>代码参考如下所示:</p><pre> </pre><ol> <li>using System; </li> <li>using System.Collections.Generic; </li> <li>using System.Linq; </li> <li>using System.Text; </li> <li>using Implementation; </li> <li>using Interfaces; </li> <li> </li> <li>namespace Creators </li> <li>{ </li> <li>public class TypeCreator </li> <li> { </li> <li>public static IDataRetriever CreateDataRetriever() </li> <li> { </li> <li>return new DataRetriever(); </li> <li> } </li> <li> } </li> <li>} </li></ol><p>在Client工程中,移除对Implementation工程的引用,添加对Creators工程的引用。</p><p>修改Program.cs,使用刚才新加的方法来创建对象。代码参考如下所示:</p><pre> </pre><ol> <li>using System; </li> <li>using System.Collections.Generic; </li> <li>using System.Linq; </li> <li>using System.Text; </li> <li>using Interfaces; </li> <li>using Creators; </li> <li> </li> <li>namespace Client </li> <li>{ </li> <li>class Program </li> <li> { </li> <li>static void Main(string[] args) </li> <li> { </li> <li>IDataRetriever dr = new TypeCreator.CreateDataRetriever(); </li> <li>IData data = dr.Data; </li> <li> } </li> <li> } </li> <li>} </li></ol><p>修正代码,解决错误问题。</p><p>重 新编译Solution,并重新打开FirstLayerDiagram,用鼠标右键单击,在菜单中执行“Validate Architecture”命令。这样我们就不是直接通过实做的类进行信息的存取,而是经由Factory取得符合接口定义的内容。再做一次层验证,我们 会看到所有的错误都消失了。</p><p>总结: 通过使用Visual Studio 2010层关系设计架构,我们就可以在开始阶段通过层关系图来进行逻辑设计,并努力执行设计方案,保证开发阶段与设计不偏离,通过自动化(例如门控签入) 进行强制执行,使团队人员的代码不漂移出架构,从而避免“漂移”发生。另外,采用Layer Diagram来验证代码架构的方法,大型项目也可以通过相同的方式进行验证。这包括如何将代码映射到层上,以及如何通过手动的方式来验证代码是否遵守定 义的约束关系,也可以在编译代码的过程中自动地进行验证。</p></div>