现在的位置: 首页 > 综合 > 正文

《大话设计模式》之–第15章 就不能不换DB吗?—-抽象工厂模式

2013年03月01日 ⁄ 综合 ⁄ 共 8023字 ⁄ 字号 评论关闭

15 就不能不换DB吗?----抽象工厂模式

15.1就不能不换DB吗?

“这么晚才回来,都11点了。”大鸟看着刚推门而入的小菜问道。

“我了个去~没办法呀,工作忙。”小菜叹气说道。

“怎么会这么忙。加班有点过头了呀。”

“都是换数据库惹的祸叹。”

“怎么了?

“我本来写好了一个项目,是给一家企业做的电子商务网站,是用SQLServer作为数据库的,应该说上线后除了开始有些小问题,基本都还可以。而后,公司接到另外一家公司类似需求的项目,但这家公司想省钱,租用了一个空间,只能用Access,不能用SQL Server,于是就要求我今天改造原来那个项目的代码。”

“哈哈,你的麻烦来了。”

“是呀,那是相当的麻烦。但开始我觉得很简单呀,因为地球人都知道,SQL ServerAccessADO.NET上的使用是不同的,在SQL Server上用的是System.Data.SqlClient命名空间下的SqlConnectianSqlCommandSqlParameter SqlDataReaderSqlDataAdapter,而Access则要用System.Data.OleDb命名空间下的相应对象,我以为只要做一个全体替换就可以了,哪知道,替换后,错误百出。”

“那是一定的,两者有不少不同的地方。你都找到了些什么问题?

“实在是多呀。在插入数据时Access必须要insert intoSQL Server可以不用into的,SQL Server中的GeDate()Access中没有,需要改成Now()SQL Server中有字符串函数Substring,而Access中根本不能用,我找了很久才知道,可以用Mid,这好像是VB中的函数口”

“小菜还真犯了不少错呀,insert into这是标准语法,你干吗不加into,这是自找的麻烦。”

“这些问题也就罢了,最气人的是程序的登录代码,老是报错,我怎么也找不到出了什么问题,搞了几个小时口最后才知道,原来Access对一些关键字,例如password是不能作为数据库的字段的,如果密码的字段名是passwordSQL Server中什么问题都没有,运行正常,在Access中就是报错,而且报得让人莫名其妙。”

“‘关键字’应该要用‘[’和‘]’包起来,不然当然是容易出错的。”

“就这样,今天加班到这时候才回来。”

“以后你还有的是班要加了。”

“为什么?

“只要网站要维护,比如修改或增加一些功能,你就得改两个项目吧,至少在数据库中做改动。相应的程序代码都要改,甚至和数据库不相干的代码也要改,你既然有两个不同的版本,两倍的工作量也是必然的。”

“是呀,如果哪一天要用Oracle数据库,估计我要改动的地方更多了。”

“那是当然,OracleSQL语法与SQL Server的差别更大。你的改动将是空前的。”

“大鸟只会夸张,哪有这么严重,大不了再加两天班就什么都搞定了。”

“哼”,大鸟笑着摇了摇头,很不屑一顾,“菜鸟程序员碰到问颐,只会用时间来摆平,所以即使整天加班,老板也不想给菜鸟加工资,原因就在于此。”

“你什么意思嘛!”小菜气道,“我是菜鸟我怕谁。”接着又拉了拉大鸟,“那你说怎么搞定才是好呢?

“知道求我啦,”大鸟端起架子,“教你可以,这一周的碗你洗。”

“行,”小菜很爽快地答应道,“在家洗碗也比加班熬夜强。”

15.2最基本的数据访问程序

“那你先写一段你原来的数据访问的做法给我看看。”

“那就用增加用户和得到用户为例吧。”

 

“我最开始就是这样写的,非常简单。”

“这里之所以不能换数据库,原因就在于SqlServerUser su = new SqlServerUser()使得su这个对象被框死在了SQL Server上了。如果这里是灵活的,专业点的说法,是多态的,那么在执行su.insert(user)su.getUser(1)时就不用考虑是在用SQL Server还是在用Access。”

“你的意思我明白,你是希望我用工厂方法模式来封装new SqlServerUser()所造成的变化?”

“小菜到了半夜,还是很清醒嘛,88错。工厂方法模式是定义一个用户创建对象的接口,让子类决定实例化哪个类,试试看吧。”

“中!”

15.3用了工厂方法模式的数据访问程序

小菜很快给出了工厂方法实现的代码。

代码结构图

 

“大鸟,来看看这样写成不?”

“非常好。现在如果要换数据库,只需要把new SqlServerFactory()改成new AccessFactory(),此时由于多态的关系,使得声明IUser接口的对象iu事先根本不知道是在访问哪个数据库,却可以在运行时很好地完成工作,这就是所谓的业务逻辑与数据访问的解耦。”

“但是,大鸟,这样写,代码里还是有指明new SqlServerFactory()啊,我要改的地方,依然很多。”

“这个先不急,待会再说,问题还没有完全解决,你的数据库里面不可能只有一个User表吧,很可能有其他表,比如增加部门表(Department表),此时如何办呢?”

“啊,我觉得那要增加好多的类了,我来试试看。”

“多写些类有什么关系,只要能增加灵活性,以后就不用加班了。小菜好好加油。”

15.4用了抽象工厂模式的数据访问程序

小菜再次修改代码,拉回了关于部门表的处理。

代码结构图

 

“大鸟,这样就可以了,只需要改IFactory factory = new AccessFactory()IFactory factory = new SqlServerFactory(),就可以实现了数据库访问的切换了。”

“很好嘛,实际上,在不知不觉间,你已经通过需求的不断演化,重构出了一个非常重要的设计模式。”

“这不就是刚才的工厂方法模式吗?”

“只有一个User类和User操作类的时候,是只需要工厂方法模式的,但现在显然你的数据库中有很多的表,而SQL ServerAccess又是两大不同的分类,所以解决这种涉及到多个产品系列的问题,有一个专门的工厂模式叫抽象工厂模式。”

15.5抽象工厂模式

抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

抽象工厂模式(Abstract Factory)结构图

AbstractProductAAbstractProductB是两个抽象产品,之所以为抽象,是因为它们都有可能有两种不同的实现,就刚才的例子来说就是UserDepartment,而ProductA1ProductA2ProductB1ProductB2就是对两个抽象产品的具体分类的实现,比如ProductA1可以理解为是SqlServerUser,而ProductB1AccessUser。”

“这么说,IFactory是一个抽象工厂接口,它里面应该包含所有的产品创建的抽象方法。而ConcreteFactory1ConcreteFacotry2就是具体的工厂了。就像SqlServerFactoryAccessFactory一样。”

“理解的非常正确。通常是在运行时刻再创建一个ConcreteFactory类的实例,这个具体的工厂再创建具有特定实现的产品对象,也就是说,为创建不同的产品对象,客户端应该使用不同的具体工厂。”

15.6抽象工厂模式的优点和缺点

“这样做有虾米好处?”

“最大好处在于易于交换产品系列,由于具体工厂类,例如IFactory factory = new AccessFactory(),在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。我们的设计不能去防止需求的更改,那么我们的理想便是让改动变得最小,现在如果你要更改数据库访问,我们只需要更改具体的工厂就可以做到了。第二大好处在于,它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。事实上,你刚才写的例子,客户端所认识的只有IUserIDepartment,至于它是用SQL Server来实现还是用Access来实现就不知道了。”

“啊,我感觉这个模式把开放-封闭原则,依赖倒转原则发挥到极致了。”

“木啦木啦,木那么夸张的说,应该说就是这些设计原则的良好运用。抽象工厂模式也有缺点。你想的出来吗?”

“想不出来,我感觉它已经很好用了,哪有什么缺点?”

“是个模式就会有缺点的,都有不适用的时候,要辩证地看待问题啊。抽象工厂模式可以很方便地切换两个数据库访问的代码,但是如果你的需求来自增加功能,比如我们现在要增加项目表Project,你要改动哪些地方?”

“啊,那就要至少增加三个类,IprojectSqlServerProjectAccessProject,还需要更改IFactorySqlServerFactoryAccessFactory才可以完全实现。啊,要改三个类,这太糟糕了的说。”

“是啊,这是非常糟糕的说。”

“还有啊,就是刚才问你的,我的客户端程序类显然不会只有一个啊,有很多地方都在使用IUserIDepartment,而这样的设计,其实在每一个类的开始都需要声明IFactory factory = new SqlServerFactory(),如果我有100个调用数据库访问的类,是不是就要更改100IFactory factory = new AccessFactory()这样的代码才行啊?这不能解决我要更改数据库访问时,改动一处就完全更改的要求啊!”

“改就改啊,公司花那么多钱养你干嘛啊?不就是要你努力地工作吗?100个改动,不算难的,加个班,什么都搞定了。”

“球球蛋,你讲过,编程是门艺术,这样大批量地改动,显然是非常丑陋地做法。我需要地是一个非常优雅地解决方案,我来想想办法改进一下这个抽象工厂模式。”

“好,小伙子,有立场,有想法,不向丑陋地代码低头,那就等你的好消息啦。”

15.7用简单工厂来改进抽象工厂

十分钟后,小菜给出了一个改进方案。去除IFactorySqlServerFactoryAccessFactory三个工厂类,取而代之的是一个DataAccess类,用一个简单工厂模式来实现。

代码结构图