为什么所有Active Record都讨厌?
随着我对OOP的了解越来越多,并开始实现各种设计模式,我不断回到人们讨厌Active Record的情况。
人们常常说它的伸缩性不好(以Twitter为例),但实际上没有人解释为什么伸缩性不好。和/或者如何在没有缺点的情况下实现AR的优点(通过类似但不同的模式?)
希望这不会变成关于设计模式的圣战-我想知道的是,具体来说,**** Active Record有什么问题。
如果伸缩性不好,为什么不呢?
还有什么其他问题?
解决方案
回答
关于Active Record的投诉,我主要看到的是,当我们围绕一个表创建一个模型,并选择了该模型的多个实例时,基本上就是在执行" select * from ..."。这对于编辑记录或者显示记录是很好的,但是,例如,如果要显示数据库中所有联系人的城市列表,则可以"从...中选择城市",并且仅获取城市。使用Active Record进行此操作将要求我们选择所有列,但仅使用City。
当然,不同的实现将对此进行不同的处理。然而,这是一个问题。
现在,我们可以通过为要尝试执行的特定操作创建一个新模型来解决此问题,但有些人会认为这比付出的努力多于收益。
我,我挖了活动记录。 :-)
高温超导
回答
我看到的Active Records问题是,它总是只有一张表。没关系,只要我们只使用一个表即可,但是在大多数情况下,使用数据时,我们将在某处进行某种联接。
是的,在性能方面,连接通常比根本没有连接要糟糕,但是通过首先读取整个表A,然后使用获得的信息来读取和过滤表B,连接通常比"伪"连接要好。
回答
我喜欢SubSonic只做一栏的方式。
任何一个
DataBaseTable.GetList(DataBaseTable.Columns.ColumnYouWant)
, 或者:
Query q = DataBaseTable.CreateQuery() .WHERE(DataBaseTable.Columns.ColumnToFilterOn,value); q.SelectList = DataBaseTable.Columns.ColumnYouWant; q.Load();
但是,对于延迟加载,Linq仍然是国王。
回答
有ActiveRecord设计模式和ActiveRecord Rails ORM库,还有.NET和其他语言的大量仿制品。
这些都是不同的东西。他们大多遵循该设计模式,但是以许多不同的方式对其进行扩展和修改,因此在有人说" ActiveRecord很烂"之前,需要先说"哪个ActiveRecord上有堆?"来进行限定。
我只熟悉Rails的ActiveRecord,我将尝试解决所有因使用它而引起的投诉。
@BlaM The problem that I see with Active Records is, that it's always just about one table
代码:
class Person belongs_to :company end people = Person.find(:all, :include => :company )
这会使用Companies.id = person.company_id上的" LEFT JOIN公司"生成SQL,并自动生成关联的Company对象,因此我们可以执行" people.first.company",并且不需要访问数据库,因为数据已经存在当下。
@pix0r The inherent problem with Active Record is that database queries are automatically generated and executed to populate objects and modify database records
代码:
person = Person.find_by_sql("giant complicated sql query")
不建议这样做,因为它很丑陋,但是对于我们只需要简单地编写原始SQL的情况,就很容易做到。
@Tim Sullivan ...and you select several instances of the model, you're basically doing a "select * from ..."
代码:
people = Person.find(:all, :select=>'name, id')
这只会从数据库中选择名称和ID列,映射对象中的所有其他"属性"都将为nil,除非我们手动重新加载该对象,依此类推。
回答
@布莱姆:
有时我只是为了加入而实现了一个活动记录。并非总是必须是表<-> Active Record的关系。为什么不使用" Join语句的结果" <-> Active Record?
回答
ActiveRecord的问题在于它为我们自动生成的查询可能会导致性能问题。
我们最终会采取一些不直观的技巧来优化查询,这使我们想知道首先手动编写查询是否会更省时。
回答
我一直发现ActiveRecord适用于Model相对平坦(例如,没有很多类层次结构)的基于CRUD的快速应用程序。但是,对于具有复杂OO层次结构的应用程序,DataMapper可能是更好的解决方案。尽管ActiveRecord假定表和数据对象之间的比例为1:1,但是这种关系在更复杂的域中变得难以处理。在关于模式的书中,马丁·福勒(Martin Fowler)指出,ActiveRecord在模型非常复杂的情况下会崩溃,并建议使用DataMapper作为替代方案。
我发现这在实践中是正确的。在域中有很多继承的情况下,将继承映射到RDBMS的难度比映射关联或者组合的难度更大。
我这样做的方法是让控制器通过这些DataMapper(或者"服务层")类访问"域"对象。它们不直接镜像数据库,而是充当某些实际对象的OO表示。假设域中有一个User类,并且需要具有对其他对象的引用或者其他对象的集合,这些对象在我们检索该User对象时已经加载。数据可能来自许多不同的表,而ActiveRecord模式可能会使它变得非常困难。
例如,控制器代码通过直接调用UserMapper.getUser()方法的API来检索User对象,而不是直接加载User对象并使用ActiveRecord样式的API访问数据。正是那个映射器负责从它们各自的表中加载任何关联的对象,并将完成的用户"域"对象返回给调用者。
本质上,我们只是添加了另一层抽象以使代码更易于管理。DataMapper类是否包含原始的自定义SQL,还是对数据抽象层API的调用,甚至是自己访问ActiveRecord模式,都与接收良好的,填充的User对象的控制器代码无关紧要。
无论如何,这就是我的方法。
回答
尽管关于SQL优化的所有其他注释当然都是有效的,但我对活动记录模式的主要抱怨是,这通常会导致阻抗不匹配。我喜欢保持域整洁并正确封装,而活动记录模式通常会破坏所有的执行希望。
回答
The question is about the Active Record design pattern. Not an orm Tool.
最初的问题是用rails标记的,并且是指使用Ruby on Rails构建的Twitter。 Rails中的ActiveRecord框架是Fowler Active Record设计模式的实现。
回答
我认为人们为什么在ActiveRecord上"讨厌"与它有什么"错误"之间可能有非常不同的原因。
在仇恨问题上,对Rails相关的任何事物都有很多毒液。至于什么地方出了问题,很可能就像所有技术一样,在某些情况下它是一个不错的选择,在某些情况下还有更好的选择。根据我的经验,我们无法充分利用Rails ActiveRecord的大多数功能的情况是数据库的结构不良。如果我们访问的数据没有主键,并且违反了第一种标准格式,即需要大量存储过程来访问数据,那么最好使用SQL包装程序。如果数据库结构相对良好,则ActiveRecord可以让我们利用它。
要添加主题,以回复带有代码段重新连接的ActiveRecord困难的评论者
@Sam McAfee Say you have a User class in your domain, and need to have references to, or collections of other objects, already loaded when you retrieve that User object. The data may be coming from many different tables, and an ActiveRecord pattern can make it really hard.
user = User.find(id, :include => ["posts", "comments"]) first_post = user.posts.first first_comment = user.comments.first
通过使用include选项,ActiveRecord使我们可以覆盖默认的延迟加载行为。
回答
尝试建立多对多态关系。没那么容易。尤其是当我们不使用STI时。
回答
我的回答很长而且很迟,甚至还不完整,但是很好的解释为什么我讨厌这种模式,观点甚至情绪:
1)简短版本:Active Record在数据库和应用程序代码之间创建"强绑定"的"薄层"。这根本解决不了逻辑,没有任何问题,也没有任何问题。恕我直言,它不提供任何值,只是为程序员提供了一些语法糖(然后它可以使用"对象语法"访问关系数据库中存在的某些数据)。最好(IMHO ...)为程序员创造一些舒适感的工作应该投资在低级数据库访问工具上,例如简单,简单,简单的hash_map get_record(字符串id_value,字符串table_name,字符串id_column_name =" id")和类似方法的一些变体(当然,概念和优雅程度会随所使用的语言而异)。
2)长版本:在任何具有"概念控制"功能的数据库驱动项目中,我都避免使用AR,这很好。我通常会构建一个分层的体系结构(我们迟早会将软件划分为多个层,至少在大中型项目中是如此):
A1)数据库本身,表,关系,如果DBMS允许,甚至还有一些逻辑(MySQL现在也已经成熟)
A2)通常,除了数据存储之外,还有很多其他东西:文件系统(数据库中的斑点并不总是一个好的决定……),旧系统(想象自己"如何"访问它们,可能有多种选择)。不是重点...)
B)数据库访问层(在此级别上,非常欢迎使用工具方法,轻松访问数据库中的数据的助手,但是AR在这里没有提供任何价值,除了一些语法糖之外)
C)应用程序对象层:"应用程序对象"有时是数据库中表的简单行,但无论如何大多数情况下它们都是复合对象,并且添加了一些更高的逻辑,因此在此级别上花费时间在AR对象上显然是毫无用处的,这浪费了宝贵的编码时间,因为无论有无AR,这些对象的"真实价值"和"更高逻辑"都需要在AR对象之上实现!并且,例如,为什么要抽象"日志条目对象"?应用逻辑代码会编写它们,但是应该能够更新或者删除它们吗?听起来很傻,而且App :: Log("我是一条日志消息")比le = new LogEntry();更容易使用。 le.time = now(); le.text ="我是一条日志消息"; le.Insert();
。例如:在应用程序的日志视图中使用"日志条目对象"可处理100、1000甚至10000条日志行,但是迟早我们将不得不进行优化,我敢打赌,在大多数情况下,我们只会使用应用逻辑中的那个漂亮的小SQL SELECT语句(这完全破坏了AR想法。),而不是用大量的代码包装和隐藏代码将这个小语句包装在固定的AR框架中。我们浪费在编写和/或者构建AR代码上的时间本可以花在一个更加聪明的界面上,以读取日志条目列表(许多方面,无所不能)。编码人员应该敢于发明新的抽象概念,以实现适合预期应用程序的应用程序逻辑,而不是愚蠢地重新实现愚蠢的模式,乍一看听起来不错!
D)应用程序逻辑实现交互对象以及创建,删除和列出(!)应用程序逻辑对象的逻辑(否,那些任务应很少锚定在应用程序逻辑对象本身中:我们桌上的纸迹是否告诉我们我们办公室中所有其他工作表的名称和位置?忘记了列出对象的"静态"方法,这很愚蠢,这是一种糟糕的折衷方案,使人类的思维方式适合[某些并非所有AR框架一样- ] AR思维)
E)用户界面很好,我将在以下几行中写的内容非常非常非常主观,但是以我的经验来看,基于AR的项目常常忽略了应用程序的UI部分,这浪费了创建模糊抽象的时间。最终,这样的应用程序浪费了很多编码人员的时间,感觉就像是编码人员为编码人员提供的应用程序,内部和外部都倾向于技术。编码人员感觉很好(根据纸上的概念,终于完成了艰苦的工作,完成并纠正了所有错误……),并且客户"只需要了解它必须像那样",因为那是"专业"。好的,对不起,我离题了;-)
好吧,诚然,所有这些都是主观的,但这是我的经验(不包括Ruby on Rails,它可能有所不同,并且我对该方法的实践经验为零)。
在付费项目中,我经常听到要求从创建一些"活动记录"对象作为更高级别的应用程序逻辑的构建块开始的需求。以我的经验,这通常是某种借口,因为客户(大多数情况下是软件开发公司)对客户最终没有一个好的概念,大视野,概观。这些客户认为框架僵化("十年前在项目中运作良好。"),他们可以充实实体,可以定义实体关系,可以分解数据关系并定义基本的应用程序逻辑,但是随后他们停下来了。并将其交给我们,并认为这就是我们所需要的...他们通常缺乏完整的应用程序逻辑,用户界面,可用性等概念,等等...他们缺乏广阔的视野,也缺乏对细节,他们希望我们遵循AR的方式,因为..那么,为什么它在几年前曾在该项目中起作用,却使人们忙碌而沉默寡言?我不知道。但是"细节"将男人和男孩分开了,或者..最初的广告口号如何? ;-)
经过多年(十年的积极开发经验),每当客户提到"主动记录模式"时,我的警钟就会响起。我学会了尝试使他们回到基本的构想阶段,让他们三思而行,尝试让他们表现出他们的构想上的弱点,或者只是在根本不关心的情况下完全避免它们(最后,我们知道的客户还没有知道它想要什么,甚至可能以为它知道但不知道,或者试图免费将概念工作外部化至ME,这使我花费了很多宝贵的时间,几天,几周和几个月的时间,生活太短了……)。
所以,最后:这就是为什么我讨厌这种愚蠢的"活动记录模式",并且我会并且会尽可能避免它。
编辑:我什至称其为无模式。它不能解决任何问题(模式并非旨在创建语法糖)。它产生了许多问题:所有问题的根源(在这里的很多答案中都提到了..)是,它只是将良好的旧的,成熟的,功能强大的SQL隐藏在受模式定义极为严格限制的接口后面。
这种模式用语法糖代替了灵活性!
考虑一下,AR能为我们解决哪个问题?