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