petshop详解(编辑修改稿)内容摘要:

其复杂的是 Parameter 的传递,在 PetShop中,使用了大量的字符串常量来保存参数的名称。 此外, PetShop 还专门为 SQL Server 和Oracle 提供了抽象的 Helper 类,包装了一些常用的操作,如 ExecuteNonQuery、ExecuteReader 等方法。 在没有 ORM 的情况下,使用 Helper 类是一个比较好的策略,利用它来完成数据库基本操作的封装,可以减少很多和数据库操作有关的代码,这体现了对象复用的原则。 PetShop 将这些Helper 类统一放到 DBUtility 模块中,不同数据库的 Helper 类暴露的方法基本相同,只除了一些特殊的要求,例如 Oracle 中处理 bool 类型的方式就和 SQL Server 不同,从 而专门提供了 OraBit 和 OraBool 方法。 此外, Helper 类中的方法均为 static 方法,以利于调用。 OracleHelper 的类图如下: 对于数据访问层来说,最头疼的是 SQL语句的处理。 在早期的 CS 结构中,由于未采用三层式架构设计,数据访问层和业务逻辑层是紧密糅合在一起的, 因此, SQL语句遍布与系统的每一个角落。 这给程序的维护带来极大的困难。 此外,由于 Oracle使用的是 PLSQL,而 SQL Server和 Sybase 等使用的是 TSQL,两者虽然都遵循了标准 SQL的语法,但在很多细节上仍有区别,如果将 SQL语句大量的使用到程序中,无疑为可能的数据库移植也带来了困难。 最好的方法是采用存储过程。 这种方法使得程序更加整洁,此外,由于存储过程可以以数据库脚本的形式存在,也便于移植和修改。 但这种方式仍然有缺陷。 一是存储过程的测试相对困难。 虽然有相应的调试工具,但比起对代码的调试而 言,仍然比较复杂且不方便。 二是对系统的更新带来障碍。 如果数据库访问是由程序完成,在 .Net 平台下,我们仅需要在修改程序后,将重新编译的程序集 xcopy 到部署的服务器上即可。 如果使用了存储过程,出于安全的考虑,必须有专门的 DBA 重新运行存储过程的脚本,部署的方式受到了限制。 我曾经在一个项目中,利用一个专门的表来存放 SQL语句。 如要使用相关的 SQL语句,就利用关键字搜索获得对应语句。 这种做法近似于存储过程的调用,但却避免了部署上的问题。 然而这种方式却在性能上无法得到保证。 它仅适合于 SQL语句较少的场景。 不过, 利用良好的设计,我们可以为各种业务提供不同的表来存放 SQL语句。 同样的道理,这些 SQL语句也可以存放到 XML文件中,更有利于系统的扩展或修改。 不过前提是,我们需要为它提供专门的 SQL语句管理工具。 SQL语句的使用无法避免,如何更好的应用 SQL语句也无定论,但有一个原则值得我们遵守,就是 “应该尽量让 SQL语句尽存在于数据访问层的具体实现中 ”。 当然,如果应用 ORM,那么一切就变得不同了。 因为 ORM 框架已经为数据访问提供了基本的Select, Insert, Update和 Delete操作了。 例如在 NHibernate中,我们可以直接调用 ISession对象的 Save 方法,来 Insert(或者说是 Create)一个数据实体对象: public void Insert(OrderInfo order) { ISession s = ()。 ITransaction trans = null。 try { trans = ()。 ( order)。 ()。 } finally { ()。 } } 没有 SQL语句,也没有那些烦人的 Parameters,甚至不需要专门去考虑事务。 此外,这样的设计,也是与数据库无关的, NHibernate 可以通过 Dialect(方言)的机制支持不同的数据库。 唯一要做的是,我们需要为 OrderInfo 定义 hbm文件。 当然, ORM 框架并非是万能的,面对纷繁复杂的业务逻辑,它并不能完全消灭 SQL语句,以及替代复杂的数据库访问逻辑,但它却很好的体现了 “80/20(或 90/10)法则 ”(也被称为 “帕累托法则 ”),也就是说:花比较少( 10%20%)的力气就可以解决大部分( 80%90%)的问题,而要解决剩下的少部分问题则需要多得多的努力。 至少,那些在数据访问层中占据了绝大部分的 CRUD 操作,通过利用 ORM 框架,我们就仅需要付出极少数时间和精力来解决它们了。 这无疑缩短了整个项目开发的周期。 还是回到对 PetShop的讨论上来。 现在我们已经有了数据实体,数据对象的抽象接口和实现,可以说有关数据库访问的主体就已经完成了。 留待我们的还有两个问题需要解决: 数据对象创建的管理 利于数据库的移植 在 PetShop 中,要创建的数据对象包括 Order, Product, Category, Inventory, Item。 在前面的设计中,这些对象已经被抽象为对应的接口,而其实现则根据数据库的不同而有所不同。 也就是说,创建的对象有多种类别,而每种类别又有不同的实现,这是典型的抽象工厂模式的应用场景。 而上面所述的两个问题,也都可以通过抽象工厂模式来解决。 标准的抽象工厂模式类图如下: 例如,创建 SQL Server 的 Order 对象如下: PetShopFactory factory = new SQLServerFactory()。 IOrder = ()。 要考虑到数据库的可移植性,则 factory必须作为一个全局变量,并在主程序运行时被实例化。 但这样的设计虽然已经达到了 “封装变化 ”的目的,但在创建 PetShopFactory 对象时,仍 不可避免的出现了具体的类 SQLServerFactory,也即是说,程序在这个层面上产生了与SQLServerFactory 的强依赖。 一旦整个系统要求支持 Oracle,那么还需要修改这行代码为: PetShopFactory factory = new OracleFactory()。 修改代码的这种行为显然是不可接受的。 解决的办法是 “依赖注入 ”。 “依赖注入 ”的功能通常是用专门的 IoC 容器提供的,在 Java 平台下,这样的容器包括 Spring, PicoContainer等。 而在 .Net平台下,最常见的则是。 不过,在 PetShop 系统中,并不需要专门的容器来实现 “依赖注入 ”,简单的做法还是利用配置文件和反射功能来实现。 也就是说,我们可以在 文件中,配置好具体的 Factory对象的完整的类名。 然而,当我们利用配置文件和反射功能时,具体工厂的创建就显得有些 “画蛇添足 ”了,我们完全可以在配置文件中,直接指向具体的数据库对象实现类,例如。 那么,抽象工厂模式中的相关工厂就可以简化为一个工厂类了,所以我将这种模式称之为 “具有简单工厂特 质的抽象工厂模式 ”,其类图如下: DataAccess类完全取代了前面创建的工厂类体系,它是一个 sealed 类,其中创建各种数据对象的方法,均为静态方法。 之所以能用这个类达到抽象工厂的目的,是因为配置文件和反射的运用,如下的代码片断所示: public sealed class DataAccess { // Look up the DAL implementation we should be using private static readonly string path = [”WebDAL”]。 private static readonly string orderPath = [”OrdersDAL”]。
阅读剩余 0%
本站所有文章资讯、展示的图片素材等内容均为注册用户上传(部分报媒/平媒内容转载自网络合作媒体),仅供学习参考。 用户通过本站上传、发布的任何内容的知识产权归属用户或原始著作权人所有。如有侵犯您的版权,请联系我们反馈本站将在三个工作日内改正。