首页 文章

为什么所有的Active Record都讨厌? [关闭]

提问于
浏览
103

随着我越来越多地了解OOP,并开始实施各种设计模式,我不断回到人们讨厌Active Record的案例 .

通常,人们说它不能很好地扩展(引用Twitter作为他们的主要例子) - 但没有人真正解释 why 它不能很好地扩展;和/或如何在没有缺点的情况下实现AR的优点(通过类似但不同的模式?)

希望这不会变成关于设计模式的神圣战争 - 我想知道的只是特别 Active Record有什么问题 .

如果它不能很好地扩展,为什么不呢?

还有什么其他问题?

14 回答

  • 2

    @BlaM:有时我justed为连接结果实现了一个活动记录 . 并不总是必须是关系表< - >活动记录 . 为什么不“加入声明的结果”< - >活动记录?

  • 51

    我喜欢SubSonic只做一列的方式 .

    DataBaseTable.GetList(DataBaseTable.Columns.ColumnYouWant)
    

    , 要么:

    Query q = DataBaseTable.CreateQuery()
                   .WHERE(DataBaseTable.Columns.ColumnToFilterOn,value);
    q.SelectList = DataBaseTable.Columns.ColumnYouWant;
    q.Load();
    

    但是Linq在延迟加载时仍然是王道 .

  • 0

    那里有ActiveRecord the Design PatternActiveRecord the Rails ORM Library,而且还有大量的.NET和其他语言的淘汰赛 .

    这些都是不同的东西 . 他们大多遵循这种设计模式,但是以许多不同的方式扩展和修改它,所以在任何人说“ActiveRecord糟透了”之前,它需要通过说“哪个ActiveRecord,那里有堆?”来证明它 .

    我只熟悉Rails的ActiveRecord,我会尝试解决在使用它的过程中提出的所有抱怨 .

    @BlaM我在Active Records中看到的问题是,它总是只有一个表

    码:

    class Person
        belongs_to :company
    end
    people = Person.find(:all, :include => :company )
    

    这将生成带有 LEFT JOIN companies on companies.id = person.company_id 的SQL,并自动生成关联的Company对象,以便您可以执行 people.first.company ,并且由于数据已存在,因此无需命中数据库 .

    @ pix0r Active Record的固有问题是自动生成并执行数据库查询以填充对象和修改数据库记录

    码:

    person = Person.find_by_sql("giant complicated sql query")
    

    这是令人沮丧的,因为它很丑陋,但对于你只是简单而且只需要编写原始SQL的情况,它很容易完成 .

    @Tim Sullivan ......你选择了几个模型实例,你基本上是在做一个“select * from ...”

    码:

    people = Person.find(:all, :select=>'name, id')
    

    这只会从数据库中选择名称和ID列,映射对象中的所有其他“属性”将只是nil,除非您手动重新加载该对象,依此类推 .

  • 3

    我一直发现ActiveRecord适用于基于CRUD的快速应用程序,其中模型相对平坦(如同很多类层次结构) . 但是,对于具有复杂OO层次结构的应用程序,DataMapper可能是更好的解决方案 . 虽然ActiveRecord假设您的表与数据对象之间的比率为1:1,但这种关系在更复杂的域中变得难以处理 . 在他的book on patterns中,Martin Fowler指出ActiveRecord在您的模型相当复杂的情况下会发生故障,并建议使用DataMapper作为替代方案 .

    我发现这在实践中是真实的 . 如果你的域中有很多继承,那么将继承映射到RDBMS比映射关联或组合更困难 .

    我这样做的方法是让控制器通过这些DataMapper(或“服务层”)类访问“域”对象 . 它们不直接镜像数据库,而是充当某些现实世界对象的OO表示 . 假设您的域中有一个User类,并且在检索该User对象时需要已经加载了对其他对象的引用或其他对象的集合 . 数据可能来自许多不同的表,ActiveRecord模式可能会让它变得非常困难 .

    您的控制器代码不是直接加载User对象并使用ActiveRecord样式API访问数据,而是通过调用UserMapper.getUser()方法的API来检索User对象 . 映射器负责从各自的表中加载任何关联的对象,并将完成的用户“域”对象返回给调用者 .

    实质上,您只是添加另一层抽象来使代码更易于管理 . 无论您的DataMapper类是包含原始自定义SQL,还是调用数据抽象层API,甚至自己访问ActiveRecord模式,对于接收一个漂亮的,已填充的User对象的控制器代码来说并不重要 .

    无论如何,我就是这样做的 .

  • 0

    我认为人们对ActiveRecord“讨厌”的原因和“错误”的原因可能有很多不同的原因 .

    在仇恨问题上,Rails相关的任何东西都有很多毒液 . 至于它有什么问题,很可能它就像所有技术一样,有些情况下它是一个很好的选择和情况是更好的选择 . 根据我的经验,您无法利用Rails ActiveRecord的大多数功能的情况是数据库结构糟糕 . 如果您正在访问没有主键的数据,那些违反第一范式的内容,那里需要大量存储过程来访问数据,那么最好使用更多只是SQL包装器的东西 . 如果您的数据库结构相对较好,ActiveRecord可以让您利用它 .

    添加回复评论者的主题,他们说ActiveRecord中的内容很难与代码片段重新连接

    @Sam McAfee假设您的域中有一个User类,并且在检索该User对象时需要已经加载了对其他对象的引用或其他对象的集合 . 数据可能来自许多不同的表,ActiveRecord模式可能会让它变得非常困难 .

    user = User.find(id, :include => ["posts", "comments"])
    first_post = user.posts.first
    first_comment = user.comments.first
    

    通过使用include选项,ActiveRecord允许您覆盖默认的延迟加载行为 .

  • 0

    我的长期和迟到的答案,甚至不完整,但一个很好的解释为什么我讨厌这种模式,意见,甚至一些情绪:

    1)简短版本:Active Record在数据库和应用程序代码之间创建“ thin layer " of " strong binding ” . 这解决了没有逻辑,没有任何问题,根本没有问题 . 恕我直言,它没有提供任何值,除了程序员的一些 syntactic sugar (然后可能使用"object syntax"来访问存在于关系数据库中的某些数据) . 为程序员创造一些舒适的努力应该(恕我直言......)更好地投资于低级数据库访问工具,例如:简单,简单,简单和类似方法的一些变体(当然,概念和优雅因使用的语言而异) .

    2)长版本:在我拥有"conceptual control"事物的任何数据库驱动的项目中,我避免了AR,这很好 . 我通常 Build 一个 layered architecture (你迟早会将你的软件分层,至少在中型到大型项目中):

    A1)数据库本身,表,关系,甚至是DBMS允许的一些逻辑(MySQL现在也成长了)

    A2)通常,不仅仅是一个数据存储:文件系统(数据库中的blob并不总是一个好的决定......),遗留系统(想象一下你自己“如何”访问它们,许多种类可能......但那就是不是重点...)

    B)数据库访问层(在这个级别,工具方法,帮助者轻松访问数据库中的数据非常受欢迎,但AR在这里没有提供任何值,除了一些语法糖)

    C)应用程序对象层:"application objects"有时候是数据库中表的简单行,但是大多数时候它们都是 compound 对象,并且附加了一些更高的逻辑,所以在这个级别的AR对象上投入时间显然是无用的,浪费宝贵的编码员时间,因为"real value",这些对象的"higher logic"需要在AR对象之上实现 - 无论如何 - 有和没有AR!并且,例如,为什么你想要"Log entry objects"的抽象?应用程序逻辑代码会写入它们,但是应该能够更新或删除它们吗?听起来很傻, App::Log("I am a log message")le=new LogEntry(); le.time=now(); le.text="I am a log message"; le.Insert(); 更容易使用 . 例如:在您的应用程序的日志视图中使用"Log entry object"将适用于100,1000甚至10000个日志行,但迟早您将需要优化 - 我敢打赌在大多数情况下,您将只使用那个小美丽应用程序逻辑中的SQL SELECT语句(完全打破了AR的想法......),而不是将这个小语句包装在刚性固定的AR构思框架中,包含大量代码包装并隐藏它 . 编写和/或构建AR代码所浪费的时间可能已被投入到更加聪明的界面中,用于读取日志条目列表(许多方式,天空是极限) . 编码器应该 dare to invent new abstractions 来实现适合预期应用的应用逻辑,并且 not stupidly re-implement silly patterns ,一见钟情听起来不错!

    D)应用程序逻辑 - 实现交互对象的逻辑以及创建,删除和列出(!)应用程序逻辑对象(不,这些任务应该很少锚定在应用程序逻辑对象本身中:你桌面上的纸张是否告诉你在办公室里所有其他床单的名称和位置吗?忘记列出物品的“静态”方法,这很愚蠢,是为了使人类的思维方式融入而造成的不良妥协[有些不是所有AR框架 - ] AR思考)

    E)用户界面 - 好吧,我将在以下几行中写的内容是非常非常非常主观的,但根据我的经验,基于AR构建的项目经常忽略应用程序的UI部分 - 时间被浪费在创建模糊的抽象上 . 最终,这些应用程序浪费了很多编码人员的时间,感觉像编码员的编码应用程序,内部和外部的技术倾向 . 编码员感觉良好(努力工作最终完成,一切都完成并纠正,根据纸上的概念......),客户“只需要知道它需要像那样”,因为那是“专业”......好的,抱歉,我离题了;-)

    嗯,诚然,这一切都是主观的,但是我的经验(Ruby on Rails被排除在外,可能会有所不同,而且我对这种方法没有实际经验) .

    在付费项目中,我经常听到要求开始创建一些"active record"对象作为更高级别应用程序逻辑的构建块 . 根据我的经验,这个 conspicuously often 是某种借口,因为客户(大多数情况下是一家软件开发公司)没有一个好的概念,一个大的观点,一个关于产品最终应该是什么的概述 . 那些客户在僵硬的框架中思考("in the project ten years ago it worked well.."),他们可能充实实体,他们可能会定义实体关系,他们可能会破坏数据关系并定义基本的应用程序逻辑,但随后他们停下来交给你,并认为这就是你需要......他们经常缺乏应用逻辑,用户界面,可用性等等的完整概念......他们缺乏大观点,他们缺乏对细节的热爱,他们希望你遵循AR的方式事情,因为......好吧,为什么,它在几年前的那个项目中起作用,它让人们忙碌而沉默?我不知道 . 但是_1111234_将男人和男孩分开了,或者......原来的广告口号怎么样? ;-)

    经过多年(十年的积极开发经验),每当客户提到"active record pattern"时,我的警铃响起 . 我学会了试图让他们 back to that essential conceptional phase ,让他们三思而后行,试着展示他们的概念弱点,或者如果他们没有分辨就完全避免它们(最后,你知道,一个客户还不知道它想要什么,也许甚至认为它知道但不知道,或者试图将概念工作免费外化给我,这花费了我很多宝贵的时间,几天,几周和几个月的时间,活得太短了......) .

    所以,最后:这就是为什么我讨厌那种愚蠢的“主动记录模式”,而我尽可能地避免它 .

    EDIT :我甚至称之为无模式 . 它没有解决任何问题(模式不是为了创造语法糖) . 它产生了许多问题:它的所有问题的根源(在这里的许多答案中都提到了......),这是一个很好的老式开发和强大的SQL背后的接口,模式定义非常有限 .

    这种模式取代了语法糖的灵活性!

    想一想,AR为你解决了哪个问题?

  • 7

    有些消息让我感到困惑 . 一些答案是“ORM”与“SQL”或类似的东西 .

    事实上,AR只是一种简化编程模式,您可以利用域对象来编写数据库访问代码 .

    这些对象通常具有业务属性(bean的属性)和一些行为(通常用于这些属性的方法) .

    AR只是说“向这些域对象添加一些方法”到数据库相关的任务 .

    从我的观点和经验来看,我不得不说,我不喜欢这种模式 .

    乍一看它听起来不错 . 一些像Spring Roo这样的现代Java工具使用了这种模式 .

    对我来说,真正的问题只是关注OOP . AR模式会强制您以某种方式将对象的依赖项添加到基础结构对象中 . 这些infraestructure对象允许域对象通过AR建议的方法查询数据库 .

    我总是说两层是项目成功的关键 . 服务层(业务逻辑所在的位置或可以通过某种远程处理技术导出,例如Web服务)和域层 . 在我看来,如果我们为域图层对象添加一些依赖关系(不是真的需要)来解析AR模式,我们的域对象将更难与其他层或(罕见)外部应用程序共享 .

    Spring的Roo实现很有意思,因为它不依赖于对象本身,而是依赖于某些AspectJ文件 . 但是如果以后你不想使用Roo并且必须重构项目,那么AR方法将直接在你的域对象中实现 .

    另一个角度来看 . 想象一下,我们不使用关系数据库来存储我们的对象 . 想象一下,例如,应用程序将我们的域对象存储在NoSQL数据库中或仅存储在XML文件中 . 我们是否会在域对象中实现执行这些任务的方法?我不这么认为(例如,在XM的情况下,我们会将XML相关的依赖项添加到我们的域对象中......我认为真的很难过) . 那么为什么我们必须在域对象中实现关系数据库方法,如Ar模式所示?

    总而言之,AR模式听起来更简单,适用于小型和简单的应用 . 但是,当我们拥有复杂的大型应用程序时,我认为经典的分层架构是一种更好的方法 .

  • 90

    问题是关于Active Record设计模式 . 不是一个orm工具 .

    最初的问题用rails标记,并指的是用Ruby on Rails构建的Twitter . Rails中的ActiveRecord框架是Fowler的Active Record设计模式的实现 .

  • 1

    关于Active Record的投诉我看到的主要问题是,当你围绕一个表创建一个模型,并且你选择了几个模型实例时,你基本上是在做一个“select * from ...” . 这适用于编辑记录或显示记录,但如果您想显示数据库中所有联系人的城市列表,您可以“从...中选择城市”并仅获取城市 . 使用Active Record执行此操作需要您选择所有列,但仅使用City .

    当然,不同的实现方式将以不同方式处理 . 然而,这是一个问题 .

    现在,你可以通过为你想要做的具体事情创建一个新模型来解决这个问题,但有些人会认为它比收益更多的努力 .

    我,我挖积极记录 . :-)

    HTH

  • 11

    我将谈论Active Record作为设计模式,我还没有看到ROR .

    一些开发人员讨厌Active Record,因为他们阅读了关于编写干净利落的代码的智能书籍,而这些书籍指出活动记录违反了单一的resposobility原则,违反了DDD规则,域名对象应该是持久无知的,以及这些书中的许多其他规则 .

    Active Record中的第二个域对象倾向于与数据库一对一,这可能被认为是某种系统的限制(主要是n层) .

    这只是抽象的东西,我没有看到ruby on rails实际执行这种模式 .

  • 5

    我在Active Records中看到的问题是,它始终只是 one 表 . 那个_1511242会在某个地方有某种联系 .

    是的,_1111243_在性能方面通常比 no join at all 差,但 join 通常比 "fake" join 好,首先读取整个表A,然后使用获取的信息读取和过滤表B.

  • 1

    ActiveRecord的问题在于它自动为您生成的查询可能会导致性能问题 .

    您最终会做一些不直观的技巧来优化查询,让您想知道首先手动编写查询是否会更有效 .

  • 1

    虽然关于SQL优化的所有其他注释肯定都是有效的,但我对主动记录模式的主要抱怨是它通常会导致impedance mismatch . 我喜欢保持我的域清洁和正确封装,活动记录模式通常会破坏所有希望 .

  • 0

    尝试做多对多的多态关系 . 不那么容易 . 特别是当你不使用STI时 .

相关问题