Rails:多态与mixins与控制器voodoo?

时间:2011-10-03 22:31:02

标签: ruby-on-rails ruby-on-rails-3.1

所以,这就是它的缺点,这是一个新手问题,所以请耐心等待......我正在建立一个公告板类型的应用程序作为我公司的Rails概念证明应用程序。

公告牌正在取代现有功能,因此限制非常紧张。最大的限制是有三个级别的内容:董事会,主题和回复。你不能回复回复(至少,系统没有帮助你) - 这是故意的,超出了这个问题的范围。

现在,主题和回复都有共同的特征:它们既是用户生成的内容,也可以是主持人,他们有正文,他们是由用户发布的等等。同事建议我简单地将这些对象设为一个名为“帖子”的概念的专业化,然后也将这篇文章用于公告牌(因为董事会也有一个标题和内容......长篇故事)。

所以我用一个帖子模型构建了应用程序,我用控制器和路由魔法完成了其他所有操作;我有一个TopicsController和一个BoardsController(通过自定义操作在TopicsController中处理回复逻辑)。我在路由文件中构建了嵌套命名空间,以便我可以执行:http://foo.com/boards/1/topics/2等。

同事(当我没有经历过Rails,所以我接受了他的话)声称我应该做这种伪多态设计,因为我们最终会有其他内容 - 例如产品评论 - - 那将有类似的内容(用户生成的文本,可能被审核) - 如果我们可以简单地绑回一个数据库表,那将会容易得多。

他还建议我应该通过模块mixins在例如产品评论中实现“额外”功能,而不是多态性。我不确定这是对简单多态的改进,还是通过表连接实现的多态性(通过db关系将额外信息绑定在一起)。

但随着这个版本变得越来越大,我对此有疑问。它只是......感觉不对劲。首先,虽然模型中存在多态性,但我并不是100%确定最佳实现是什么;在某些方面,主题和回复都有共同的元素 - 既有内容又可以审核 - 但在其他方面它们是非常不同的(例如,回复没有主题)。最终用户甚至不创建董事会;他们是主持人创造的。产品评论与主题或回复完全不同;相反,它们与特定产品相关联。

我越是建立这个,我越觉得我们拥有“你所拥有的就是锤子”综合症;我们看到主题和回复以及董事会和产品评论都看起来“有点相似”,所以我们试图将它们强制转换为继承树。事实上,我发现自己必须更加努力地对抗Rails框架,以添加自定义路由和操作,覆盖默认模型属性等等。

所以,我的问题是:社群对此的看法是什么?我们是否应该只为董事会,主题和回复提供单独的表格和模型?

3 个答案:

答案 0 :(得分:3)

我的经验是,通常在rails中使用多态性几乎总是一个错误。几乎每当我看到有人开始使用它时,他们最终都会把它拉出来。

我所相信的是,模型越简单,一般情况下越好。因此,对于这种类型的方法,我建议使用三个ActiveRecord类 - Boards,Topics和Replies。

另外,关于嵌套名称空间 - 您是否在路由文件中将其作为嵌套资源实现?我建议过度使用命名空间。具体来说,我建议:

resources :boards do
  resource :topics
end    

运行rake routes这会为您提供以下路线:

     board_topics POST   /boards/:board_id/topics(.:format)      {:action=>"create", :controller=>"topics"}
 new_board_topics GET    /boards/:board_id/topics/new(.:format)  {:action=>"new", :controller=>"topics"}
edit_board_topics GET    /boards/:board_id/topics/edit(.:format) {:action=>"edit", :controller=>"topics"}
                  GET    /boards/:board_id/topics(.:format)      {:action=>"show", :controller=>"topics"}
                  PUT    /boards/:board_id/topics(.:format)      {:action=>"update", :controller=>"topics"}
                  DELETE /boards/:board_id/topics(.:format)      {:action=>"destroy", :controller=>"topics"}
           boards GET    /boards(.:format)                       {:action=>"index", :controller=>"boards"}
                  POST   /boards(.:format)                       {:action=>"create", :controller=>"boards"}
        new_board GET    /boards/new(.:format)                   {:action=>"new", :controller=>"boards"}
       edit_board GET    /boards/:id/edit(.:format)              {:action=>"edit", :controller=>"boards"}
            board GET    /boards/:id(.:format)                   {:action=>"show", :controller=>"boards"}
                  PUT    /boards/:id(.:format)                   {:action=>"update", :controller=>"boards"}
                  DELETE /boards/:id(.:format)                   {:action=>"destroy", :controller=>"boards"}

一般来说,每当你发现自己正在与铁杆框架作斗争时,这是一个明确的信号,你正在艰难地做事。很好的工作感知 - 并与你的直觉一起去。保持简单。

答案 1 :(得分:1)

我绝对会将电路板和产品评论作为单独的数据库表与独立的模型和控制器保持一致。他们彼此之间确实有很大的不同,不同于主题和回复。用户生成的内容和审核等功能不依赖于多态性。只要列名相同,模型基本上是自动鸭型的。您可以将审核功能包括为控制器和/或模型模块混合,并假设相关对象具有适当的属性以便审核起作用。

另一方面,主题和回复似乎是多态性的主要候选者(具体来说,我使用单表继承)。如果我理解正确,这些主题和回复几乎都是相同的,所以多态性将为您节省一些工作并防止错误(通过保持干燥)。

一般来说,它就像你说的那样。如果您觉得自己正在反对框架的意见,那么您并没有真正充分利用框架,而您应该重新构建您的设计以减少对抗。

免责声明:我并不认为自己是Rails的专家,但我也不是新手 - 毕竟我确实拥有“ruby-on-rails”stackoverflow徽章;)。我写过几个应用程序,包括两个大量利用多态性和模块混合的应用程序,所以我已经有很多经验来解决这个特殊类型的问题。

答案 2 :(得分:0)

首先,寻找合适的宝石可能是一个好主意 - 那里有大量的宝石可以简化您的代码/编码。例如acts_as_commentable,acts_as_taggable,ancestry等。

你的问题的答案是:它取决于......

如果您有某种需要施加的流量 - 例如你需要有一个主题才能发表评论,你需要在得到答复之前发表评论..等等。

使用多态自引用关联可能有效,但它会使您的设计更复杂。 也许最好是直接实现,其中每个都是他们自己的模型,然后在需要时稍后进行优化。

你可能还想看一下“祖先”的宝石,看看它们如何为祖父和孩子的快速访问建立祖先链。

检查这些RailsCasts:

ooops ... RailsCasts目前正在下降......待续......

第163集:自我指涉协会

检查http://www.railscasts.com/ - “有评论和回复的博客帖子”类型应用程序有大量示例