PEAA笔记(转载整理)
PEAA笔记
6.4. 与Row Data Gateway、Table Data Gateway的区别
1. Transaction Script
1.1. What is it
很多企业应用可以看成一系列的事务,每一个事务可以通过使用一个Transaction Script来处理。
1.2. How it works
使用Transaction Script,我们可以专注于处理好每一个事务,而不必考虑其他事务的影响,所作的就是得到输入,查询数据库,处理事务,保存结果。
Transaction Script可以有两种方法组织成类。一种是将同一领域的几个Transaction Script放在一个独立的类中;另一种是采用Command模式,将每一个Transaction Script组织成一个Command基类的子类;当然,也可以不要组织成类,而使用全局函数的方式,但是类的方式更易于隔离数据。
1.3. When to use it
Transaction Script最大的优点和缺点,都是在于它的简单性。优点是代码的简单易懂,运行效率也不错;缺点是很可能出现代码重复,当然这个可以通过重构来减轻,更重要的是,随着企业事务复杂性的增加,Transaction Script就会变得力不从心。
Transaction Script下的开发有以下特征:对象类只包括数据,不包括任何事务相关的算法,往往就是数据库中表结构的类化;调用的Sql语句常常在Transaction Script核心函数中出现,当然这个是不好的设计,应该使用比如Table Data GateWay等模式,将Sql语句封装在wrapper中,来进行数据库交互,而核心函数直接访问wrapper的方法来进行事务处理。
2. Domain Model
2.1. What is it
随着企业事务的复杂化,我们应该采用Domain Model模式来开发应用。Domain Model是一种将数据与其行为集成在一起的模式。
2.2. How it work
使用Domain Model时,我们需要在应用程序中加入一个完整的对象层,这些对象模仿业务系统中的对象及其逻辑规则,这和Transaction Script中对象仅仅是数据不包含业务逻辑形成最大的区别。
一个Simple Domain Model看上去和数据库设计非常相似,每个Domain Object对应一个数据表,可以使用Active Record模式来和数据库交互;Rich Domain Model则不然,通过使用继承,策略等模式和相互关联的对象网,Rich Domain Model可以更好的处理更复杂的逻辑,同时也增加了和数据库间映射的难度,这时可以采用Data Mapper模式来应对。
基于业务逻辑的多变性,数据对象层也应该能够更灵活的建立,修改和测试,这样,我们必须尽量减少该层同其他层次间的耦合。
2.3. When to use it
当你面对一个涉及验证,计算等等复杂多变行为的业务规则时,你需要考虑使用Domain Model。使用Domain Model时,数据库交互方面的第一选择是Data Mapper,它有助于使你的对象层同数据库独立。你也可以考虑使用Service Layer来给你的Domain Model暴露出更加清晰的API。
使用Domain Model很大的好处是,通过对象类的继承多态等性质,可以方便的扩展模型,以适应多变的企业逻辑,同时减轻重复代码的现象。
2.4. 和Transaction Script的区别
Transaction Script:从数据库读取数据 -〉 以读取的数据集作为参数,调用Transaction处理类。
Domain Model:从数据库读取数据 -〉 映射为数据对象类 -〉 调用对象类的成员函数来处理企业事务。
领域模型的思想是面向对象编程(OOP,Object-Oriented programming),事务脚本的思想是过程序编程,在复杂业务逻辑下,OOP可以应用各种设计模式,而过程序编程只能用swith和if。
3. Table Module
3.1. What is it
Table Module是处理一个数据表或者数据视图所有行的业务逻辑的一个单独的实例。
一般的,Domain Model等传统面向对象模式都建立在对象/身份的基础之上,就是说比如一个员工类的实例就对应着一个特定的员工,这样我们可以执行员工操作,获取员工信息等。这些模式的不好之处在于很难和关系数据库形成好的接口,导致我们要作大量工作用于处理数据在业务层和数据库这两个表现完全不同的层次之间的传递。
Table Module则不然,它对于数据库中的每个表建立一个类的实例,用来操作该表中的数据。
3.2. How it works
Table Module模式使得我们可以打包数据和它们的行为,并同数据库保持很好的联系。它常常作为面向表的后台数据结构,而表状的数据一般的是Sql语句调用的RecordSet返回值。
Table Module即可以使类的单独实例,也可以是类的一组静态函数。采用实例的方式给我们更大的好处,便于通过一个存在的RecordSet来初始化,也很容易的通过继承等方式进行扩展。
Table Module模式可以通过厂模式来实现请求,另外的方法是通过Table Data Gateway,不好的是引入了Table Data Gateway类和机制,好的方面是我们可以只使用一个Table Module了,对于不同的数据源,采用不同的Table Data Gateway就可以了
Table Module使用方法是:首先通过Table Data Gateway把数据装成RecordSet,然后以其为参数创建Table Module,通过Table Module来处理业务逻辑,并把修改的结果传回数据库。中间的过程中,RecordSet数据一直在内存中,而不必返回数据库。
当然Table Module并不局限于表,表/视图/Sql查询结果等都可以应用此模式。
3.3. When to use it
Table Module基于面向表的数据,而且它一定是把数据结构放在整个代码的中心。面对一个复杂的业务逻辑,它不能提供足够的实例-to-实例的能力,在处理多态上也存在不足,这时我们还是应该采用Domain Model,Domain Model+Active Record也可以处理表状的数据。
Table Module在COM应用中比较常见,这是因为微软的ADO库提供了一个很好的机制,使用RecordSet来处理数据库的数据。
Table Module是一种领域逻辑模式,一个类对应于数据库中的一个表;Table Module通常和Table Gateway合作,前者负责基本的业务逻辑,后者负责数据库访问,以达到逻辑层和持久化层的隔离;微软的实例代码经常使用这两者,如对表Person,通常会定义两个类,PersonBL和PersonDB,在PersonBL中处理验证等逻辑,并调用PersonDB访问数据库,层间调用使用DataSet或自定义数据传输对象传输数据。
4. Table Data Gateway
4.1. What is it
Table Data Gateway是扮演一个数据表的对象,通过它可以处理所有对表的访问和操作。
很多开发者并不熟悉SQL,即使那些熟悉的也不见得能够把程序写好。将操作访问数据表的CRUD操作都放在Table Data Gateway对象中,开发者就可以调用该对象的接口来和数据表进行交互。
4.2. How it works
Table Data Gateway经常是无状态的,只是负责数据从数据库进出。
使用中比较有技巧的是如何处理从请求中返回的多个项,因为很多我们使用的编程语言只能返回一个值。一个方法是使用Map,Map是一个简单的数据结构,可以把数据库的返回项映射到Table Data Gateway中,但是这个方法必须要把数据从RecordSet中拷贝出来到Map中,而且失去了编译时刻检验,接口上也不清楚。另一个方法是使用Data Transfer Object。
在Dotnet等很多使用Record Set的环境下,Table Data Gateway可以和Table Module等模式很好的使用。Table Data Gateway也可以通过返回Domain Object的方法来和Domain Model一起使用,不过这样并不好,两个模式会紧密地联系在一起,难以分离。
一般的Table Data Gateway的一个实例对应数据库中的一个表,对于一些简单的情况,你甚至可以用一个实例操作所有的表。通过Data View而不是Data Table来访问数据库也可以减少其同数据库结构之间的耦合。不过使用Data View时,常常不能够Update。
4.3. When to use it
Table Data Gateway几乎是最为简单的数据库接口,它能够很好的封装操作数据库数据的实际过程。它可以和Table Module模式很好的配合,事实上,基本想不出Table Module会使用的其他数据库接口。在Domain Model模式中很少用到它,因为Data Mapper提供了更好的数据隔离功能。
和Row Data GateWay一样,Table Data Gateway也能很好的配合Transaction Scripts使用,取舍在于你要如何处理多行的数据。很多人倾向使用Data Transfer Object,但是除非同样的Data Transfer Object在各处使用,不然使用它并没有什么好处。
5. Row Data Gateway
5.1. What is it
Row Data Gateway对应数据源中的一个数据记录,它的一个实例就是一行数据。
5.2. How it works
Row Data Gateway可以和Transaction Scripts一起和谐的工作。由于它对应一行的数据,故此在使用中会面对将查找操作放置在哪儿的问题。我们可以采用静态函数的查找操作,但是这样丢掉了通过多态来生成多个相似查找操作的可行性。另一种方法是使用Row Data Gateway的时候,搭配一个finder对象。
Row Data Gateway和Active Record比较类似,区别在于是否其中含有业务逻辑。Row Data Gateway只是包含数据访问逻辑,而没有任何业务逻辑。
使用Row Data Gateway的时候,也要注意Update数据时的逻辑。
Row Data Gateway写起代码来比较繁琐,不过使用MeatData Mapping就好多了,数据访问代码可以自动生成。
5.3. When to use it
一般的,在使用Transaction Scripts时使用Row Data Gateway;在使用Domain Model是则不使用Row Data Gateway,对于简单的数据映射,可以采用Active Record,复杂的可以采用Data Mapper,后者对数据库有很好的封装,使得Domain Object可以不必了解数据库的结构。
有趣的是,Row Data Gateway经常可以和Data Mapper一起很好的使用,虽然这看起来增加了很多额外的工作,实际上如果Row Data Gateway是在手动添加Data Mapper时通过Metadata自动生成的的话,这样做也是很有效率的。Row Data Gateway起到了Domain Object和数据库之间的隔离带的作用,可以使你在不更改业务逻辑的基础上更改数据库结构。
在Transaction Scripts中使用Row Data Gateway的时候,需要注意,如果你有一些业务逻辑在几个Scripts中都有出现的话,将业务逻辑加入到Row Data Gateway中,把Row Data Gateway变为Active Record,这将有效的减少业务逻辑的重复代码。
6. Active Record
6.1. What is it
Active Record包装了数据表或视图中的一行数据,封装了它的数据库访问行为,并加入了该数据的业务逻辑。也可以这样看,Active Record是加入了数据库访问行为的Domain Object。
6.2. How it works
Active Record即可以包含数据记录的所有业务逻辑,也可以只包含普通的面向数据的逻辑,将其余的业务逻辑通过Transaction Script来实现。
Active Record常常实现下面的方法:
1、通过Sql查询结果构造一个Active Record实例;
2、为插入数据库操作预先生成一个Active Record实例;
3、静态的Finder方法,返回Active Record对象;
4、通过Active Record对象Update数据库和Insert数据库;
5、Get/Set数据域;
6、实现一些业务逻辑;
由于Active Record和数据库的紧密耦合,我们经常在此模式中看到静态的Finder方法,当然也可以把这些Finder方法放到一个类中。
6.3. When to use it
Active Record的一个优点是比较简单,在基于单条数据记录的CRUD操作中都能很好的工作。
在Domain Model中主要的选择就是Active Record和Data Mapping,前者比较简单,而且比较适合解决对象模型恰好对应数据库中的表结构的业务领域。但是问题是如果义务逻辑比较复杂,你希望你的对象模型能够处理关系,集合,继承等等时,Active Record就力不从心了,这是就是使用Data Mapper的时候了。
Active Record的另一个问题是它把对象模型的设计紧密地和数据库结构耦合在一起,修改和重构都很困难。
如果你使用Transaction Script,那么Active Object也是一个很好的模式,它帮你减少代码重复等不好的现象。
6.4. 与Row Data Gateway、Table Data Gateway的区别
Row Data Gateway模式中每个对象也封装了数据库记录的状态和持久化到数据库的访问方法;这两个有时候很难区分。细微的区别在于Row Data Gateway不封装任何业务逻辑。
ActiveRecord的区别在于ActiveRecord的对象中保持了记录的值,是有状态的,而TableGateway是没有状态的,只是一系列数据库访问方法的集合。
7. Data Mapper
7.1. What is it
Data Mapper是数据对象和数据库之间传递数据的一个中间层,使两者保持独立。
数据对象可以包含数据和业务逻辑,数据访问逻辑有DataMapper完成,这使得数据对象和数据库可以各自使用更符合自己的方法来组织数据结构,内存中的数据对象也再不需要知道SQL和数据库,甚至Data Mapper对于业务层都是未知的。
7.2. How it works
Data Mapper可以整个被替换,或者为了测试,或者为了同一套业务逻辑使用不同的数据库的需要。
当进行数据库操作时,Data Mapper层需要知道数据库有哪些改变,以便对应到数据对象中,这样往往将整个操作放到一个事务框架中,可以使用Unit of Work模式来组织。
使用Data Mapper时,还可以使用一个查询生成多个相关对象实例。
一个应用可以有一个或者多个Data Mapper,如果硬编码mapper,那么最好每一个业务Domain类或者业务体系使用一个Mapper;如果使用MetaData Mapper,那么使用一个Mapper就可以了。对于后一种情况,复杂性转移到了Finder方法上来,一个单独的Mapper使用很多的Finder方法,我们可以将类似的Finder依据业务体系放到一个Finder类中来管理。
使用Finder类的时候,Finder类还要使用Identity Map来维护对象的唯一性。
7.3. Handling Finders
我们可以在业务系统启动的时候,将所有对象Load进内存,也可以Lazy Load,每当使用一个对象的时候,通过Finder来Load对象以增加效率。
创建一个对象时有两种方式,一种是胖构造,就是传入很多数据一下子把对象建好;一种是建一个空对象,然后通过Setter函数来赋值。前者很不错,但是一定要注意循环引用的问题,就是两个对象互相引用,,结果创建时陷入循环。解决方法是创建空对象,然后setter设置,并通过Identity Map来监督,一旦陷入循环,那么Identity Map就返回已经创建的对象,而不是继续创建对象。
7.4. Metadata Based Mappings
映射数据对象和数据库域时,一种是显式的映射,就是明确将对象属性和数据库域关系硬编码;另一种是将原数据也一起作为数据传递,这样可以通过原数据来处理映射关系。
7.5. When to use it
使用Data Mapper的主要作用是,使Domain Model可以无视数据库,无论是在设计,创建还是测试的时候。编码人员只要了解业务逻辑,而不必关心数据的存储方式。缺点当然是多出的一个Mapper层。
当不使用Domain Model的时候,一般也不会使用Data Mapper。