anti-Pattern # 1: 紧耦合

松散耦合比紧耦合困难,并且通常性能比较差。  
您从一开始是好意,但最终你会问得到的好处是否值回成本。
 可以只创建类的一个实例和直接调用该方法时,为什么引入接口和依赖注入?
 也可以填充 DataTable,并将其传到各层,为什么要用ORM呢?

更糟糕的是,您通常不认为紧耦合会有什么痛苦。 在短期来说,您获得一些效率,并完成了任务,
但从长远发展,应用程序会变得几乎不可能进化。在某种情况下,两个模块是紧密配合而密不可分时,用紧耦合是正确的。
但其他时候,还是让模块之间保持一定距离为好

一个特别常见和痛苦反模式示例,是使用的TableAdater从数据库和 Web 服务检索数据,用DataSet传递到客户端 的体系结构。  
 DataSet和数据库具有相同的架构 ,这样会使得数据库和客户端紧耦合,
这种方式构建系统,对系统的任何部分的修改都会影响到所有其他部件。  

anti-Pattern # 2: 假设需求是不变的

有时您设计系统是围绕着一个假设,客户的要求将不会改变。但在两种情况,变化的需求具有尤其重要的影响。
你相信客户的需求是正确的(但有时是不正确的);你假定客户端将使用特定技术实现(比如固定的浏览器)。

虽然你的假设(任务边界)不太可以突然变化,但一旦发生,你的数据完整性,安全会产生严重的后果。比如你假定界面是Web/Html,你只做了JavaScript的校验,而你的中间层信任你从UI传过来的数据,没有再次校验而直接存到数据库里面,
可能会发生比你想象严重很多的后果,不要以为你的程序运行在内网就安全了。有人可能创建了另一个客户端来使用你的service,或者在你原来的客户端的不同页面去掉验证来调用。一切皆有可能。
 
一旦你建立了service,一定要规范它的使用,按传统经验来看,你在每个中间层都应该一直验证安全,尽管有些重复。

第二个问题,固定客户端在某个特定的技术,更有可以成为一个问题。技术总是不断更新。如果这个应用活得足够长,总会跟随着技术变革而作一些改变,特别是客户端是最容易受影响的。如果你一开始用winform/smart client设计的的应用,过一段时间你需要迁移到手机或者Silverlight,如果你的service通过dataset传递数据,这种情况下,你要给你的service和客户端动大手术了


Anti-Pattern #3: 错误处理并发
并发是复杂而重要的领域,尽管用DataSet交换数据有紧耦合的缺点,但Dataset处理得很好,用适配器更新数据时,在更新前它会检查新旧值,如果不一样会抛出异常。

很不幸,许多开发人员并不懂得处理并发的微妙之处,使得情况更糟糕。一种常见的并发错误是把应用部署到生产环境才出现问题,幸运的话是产生明显的错误,不幸的话就是对你的数据产生了严重的破坏,而过了很久你才能发现 。并发管理的核心相当简单:就算2个用户同时修改同一个数据,也要保证数据完整性。有些读者提出这些问题,即使不是 n 层架构,也会出现。


 大多数的应用程序并发操作选择的管理技术是开放式并发。 即使多个客户端可能会同时访问数据库,但同时修改完全相同的实体的次数是非常小的。您假设所有应用正常工作,但需要采取措施来检测是否出现问题。

检测是由一个或多个属性组成,统称为该并发令牌,当该实体的任何部分更改时,并发令牌都会更改。 应用程序读取实体,它先保存并发标记的值。 然后当它希望将该实体写回到该数据库时,它首先检查以确保在数据库中的并发标记的值是否相同与最初读取的值相同, 如果相同,更新将继续;否则将停止更新并引发异常。
 
请参考下面的文档:
http://developer.51cto.com/art/201010/229106.htm
http://blog.csdn.net/xfans12000/archive/2007/03/02/1519699.aspx
http://ayende.com/Blog/archive/2009/04/15/nhibernate-mapping-concurrency.aspx

唯一能够同时保持高并发和高可伸缩性的方法就是使用带版本化的乐观并发控制。版本检查使用版本号、 或者时间戳来检测更新冲突(并且防止更新丢失)。


anti-Pattern 4: 状态服务
下一个反模式出现在,当开发人员尝试通过在多层传递Context(上下文)来简化操作。 这看起来很好,最初因为它避开并发性问题。 如果您保留上下文活动在 mid-tier,它将包含正确的原始实体值。 当您从客户端回收到实体时, 可以比较与上下文中的该实体的版本更新的实体,并应用适当的更改。 如果保存该实体,则进行正确的并发检查,并没有额外的数据库查询。
虽然这种方法似乎表面上轻松,但有许多隐藏问题需要处理。 管理上下文生存期很快就成为棘手的问题。 必须维护调用该服务的多个客户端之间的不同上下文。 即使您解决这些问题,最后您将面对其最主要的可伸缩性问题。

这些可伸缩性问题不只是每个客户端都占用服务器资源的问题。 还必须创建Context的过期方案,以防止客户启动context但永远不关闭。 进一步,如果您决定,需要扩展您的解决方案到多个服务器,则您将不得不维护Context在多个服务器的状态问题。

你花了大量工作和特殊的技术来解决这些问题时,实际上,最佳的解决方案是让这些服务实现无状态。 每次进行服务调用,在 中间层创建必需的资源,处理呼叫,然后释放特定于该调用的所有资源。 如果某些信息需要被多层调用,信息应通过客户端保留,而不是在中间层,这样没有会话关联,不会出现上面的问题。

anti-Pattern #5: 两层架构假装为三层

相当经常遇到的另一个反模式还是尝试简化过程。 通常它显示为类似这样的请求,"为什么实体框架不能把查询序列化到其他层?"然后立即有下一个问题,"它可以支持从另一层开始的更新吗?"

这些可能是 Microsoft 将添加到实体框架的功能,但如果您停下来花一分钟考虑,您将考虑这是否是个好主意。

如果客户端层上,可以创建实体框架的ObjectContext,执行Context的查询中加载实体、 修改这些实体和通过中间层把修改提交到数据库。如果这样做的话,为什么需要中间层呢? 为什么不直接公开数据库?

请记住 Fowler 的分布式对象第一个法则。 请记住,多层体系结构的唯一意义是您真正,真正需要时。 如果您真正需要它,则需要更好的安全性或和扩展到多个的服务器的能力,而不仅仅是因为公司的开发指引是要多层架构,你就引用多一层作为数据库的代理。 我建议是在满足特定需要时,使用完全的多层架构,或者如果可以的话,放弃多层,2层就足够了。

anti-Pattern #6: 低估简单的重要性

务必考虑您的目标,是否需要投资 n  层架构。 简单很好。 有时两层架构已经足够了。
有时您需要更多层,但要确保所有东西都受您的控制


原文地址:http://msdn.microsoft.com/en-gb/magazine/dd882522.aspx

posted on 2010-10-12 17:43  Gu  阅读(415)  评论(0编辑  收藏  举报