在本系列的第一篇博文中,我将VSTS 2010 架构师版本分解成了几个功能模块(如下图所示),并在随后的几篇博文中介绍了其中的几个模块(下图中的高亮部分)。今天,我想重点介绍一下Layer Diagram。具体的说,我想给大家演示一下如何创建一个Layer Diagram,如何将你的代码映射到图上,以及如何手动的使用你在图上创建的约束来对你的代码进行验证。(在未来的文章中,我将给大家演示如在你的编译过程自动地进行验证)

在本文中,我将使用一段非常简单的代码,因为这里我想强调的是代码所代表的概念,而不是代码的细节。让我们开始吧……
1. 启动VSTS 2010
2. 创建一个名为Client的C# 控制台程序,并将Solution 命名为Layer Validation。你的“New Project”对话框应该如下图所示:

3. 现在右键单击Solution节点,选择“New Project…”,在弹出的对话框中选择“Class Library”并将工程命名为Implementation:

4. 重复第三步,创建名为Interfaces和Creators的Class Library工程。现在你的Solution Explorer应该如下图所示:

5. 展开Interfaces工程节点,右键点击Class1.cs,选择“Rename”,将该文件重命名为IDataRetriever.cs,并在弹出的对话框中选择“Yes”。你的文档编辑窗口和Solution Explorer应该如下图所示:

6. 把class关键字改为interface,将IDataRetriever变成一个接口。
7. 为IDataRetriever添加一个getter属性,该属性返回一个IData类型的对象。你会注意到IData下面的红色波浪线,表示IData不存在。

8. 让我们使用VS 2010的一个新功能来解决这个问题。
9. 右键点击出错的IData,选择“Generate”,然后选择“Other…”

10. 你将看到一个“New Type”的对话框。将其中的“Kind:”修改为“interface”,“Access:”修改为“public”。其他的保留默认设置:

VS会自动向Interfaces工程添加一个IData.cs文件,并在文件中创建一个名为IData的接口。太好了,正是我们想要的!下面让我们在Implementation工程中实现IDataRetriever接口。
11. 展开Implementation工程节点,右键单击References节点,选择“Add Reference…” ,在弹出的对话框中选择Project页,然后选择Interfaces工程。
12. 将Class1.cs重命名为DataRetriever.cs。
13. 打开DataRetriever.cs文件,修改DataRetriever类使其实现IDataRetriever接口。你会注意到当输入IDataRetriever的时候没有出现Intellisense支持。没关系,我们可以手动输入IDataRetriever,然后你会发现IDataRetriever下面又出现了红色波浪线。
14. 将鼠标移动到IDataRetriever上,你会注意到在这个单词开始的位置下方有一个方形的小图标。点击它并选择“using Interfaces;”。它会自动为你添加所需的using语句:

15. 现在“using Interfaces”已经自动为你添加好了。再次选中这个图标,不过这次选择“Implement interface ‘IDataRetriever’”命令。

现在你的“DataRetriever”文件看起来如下图所示:

16. 接下来向Client工程添加到Implementation和Interfaces工程的引用。你可以参考前面第11步的步骤。
17. 打开Client工程中的Program.cs文件。
18. 输入下图所示的代码:

19. 重新编译整个Solution。
让我们来看一下这段程序究竟做了什么。它创建了一个对象,调用了对象的一个属性,然后抛出一个“NotImplementationException”异常。看起来似乎不是那么有趣。但这不是我们关注的重点。这段程序描述了一个实际系统经常遇到的问题。
在这段代码中,Client工程直接访问了一个接口(IDataRetriever)的实例(DataRetriever)。这在目前看来是没有问题的,因为目前所有的数据都是从DataRetriever中获取的(你可以想象DataRetriever是从一个SQL数据库中获取数据)。但是如果将来我们需要从另一种数据源中获取数据,我们如何才能在不改动应用程序其他部分的情况下实现这个需求呢?
我们需要的是不对接口的具体实现做任何架设,而仅仅依赖于接口本身。这是一个相当普遍的设计模式,但是在现实应用中很容易被违反。只要一行错误的代码就会破坏这个模式,建立模块间不必要的依赖关系。我们通常使用控制反转(IoC)来解决这个问题。
那么如何才能保证我们的代码不会违反这一设计呢?我们可以使用Layer Diagram和Layer Validation来帮助我们。
首先让我们创建一个Layer Diagram来可视化地描述我们在架构中想要维护的约束关系。
创建Layer Diagram
1. 点击主菜单的“Architecture”菜单项,选中“New Diagram…”,在弹出的对话框中选择“Layer Diagram”,并将图命名为“FirstLayerDiagram.layerdiagram”,如下图所示:

2. 在弹出的对话框中,将工程命名为“FirstModelingProject”:

3. 现在让我们来画一张Layer Diagram,如下图所示:

请注意箭头的方向必须如图中所示,否则后面的步骤将无法正常工作!
通过这张图我们想要表示的是Client Logic层依赖于Interfaces层,Implementation层同样依赖于Interfaces层。但是Client Logic层和Implementation层之间没有依赖关系。这个约束关系是非常重要的。
现在我们需要将我们的代码映射到Layer Diagram上,这样系统就能验证我们的代码是否符合图中描述的约束关系。
将代码映射到层上
1. 在Solution Explorer中,选中Client工程并将它拖拽到Layer Diagram上的Client Logic层上:

2. 将Interfaces工程拖到Interfaces层上:

3. 最后,将Implementation工程拖到Implementation层上:

4. 现在你的Layer Diagram看起来应该如下图所示。注意层左上角的数字“1”表示该层已经和一个工程相关联。
5. 如果你选中Client Logic层,再打开Layer Explorer,就可以看到和当前层关联的项目。在这里,是Client.exe。
现在我们可以用这张图来对我们的代码进行验证了。
6. 右键单击Layer Diagram的任何位置,选择“Validate Architecture”
你会注意到“Error List”窗口中有三条错误信息:
这是可以预见的,因为Client工程中的Program.cs直接使用了Implementation工程中定义的类型。而在我们刚才创建的图中,这种依赖关系是错误的!
现在让我们来解决这个问题。
修正代码
回到Program.cs,我们需要确保只使用Interfaces工程中定义的类型。换句话说,我们不能直接使用Implementation工程中定义的类型。我们需要在不产生直接依赖关系的情况下创建实现IDataRetriever接口的对象。
让我们引入工厂(Factory)模式来解决这个问题。
1. 在Solution Explorer中,展开Creators工程,将Class1.cs重命名为TypeCreator.cs。
2. 向Creators工程添加对Implementation和“Creators”工程的引用(换句话说,Creators工程现在依赖于Implementation和Interfaces工程)。
3. 打开TypeCreators.cs,向其中添加一个静态方法,该方法返回一个IDataRetriever的对象。代码如下图所示:

4. 在Client工程中,移除对Implementation工程的引用,添加对Creators工程的引用。
5. 修改Program.cs,使用我们刚才新加的方法来创建对象。如下图所示:

6. 重新编译Solution。
7. 现在重新打开FirstLayerDiagram,右键执行“Validate Architecture”。你会看到所有的错误都消失了。
总结
通过一个简单的例子,我给大家演示了如何使用一个简单的Layer Diagram来验证你的代码。这包括如何将代码映射到层上,以及如何通过手动的方式来验证你的代码是否遵守定义的约束关系。
Layer Diagram还有许多其它的功能。其中一个重要的功能是如何在编译代码的过程自动地进行验证。我将在下一篇博文中为大家介绍。
注:本文部分翻译自Cameron Skinner的博文:Layer Validation with the VSTS 2010 CTP