【问题标题】:Rails best practice for similar controllers/views?Rails 类似控制器/视图的最佳实践?
【发布时间】:2013-05-29 02:21:00
【问题描述】:

我正在编写一个库存管理 Rails 应用程序。我有class Product < ActiveRecord::Baseclass Foo < Productclass Bar < Product。 Foo 和 Bar 的行为略有不同,但使用单表继承对它们来说非常有用。

问题在于控制器和视图。目前我将它们 完全 分开,这可行,但包含大量重复代码。通过复制两个目录并将@foo@foosFoo 替换为@bar@barsBar,实际上可以从另一个生成一个。当我添加新功能时,添加两次会很烦人。而且,当然,不干也不是 Rails 方式。

那么这里的正确方法是什么?对于控制器...我应该创建一个ProductsController,然后只使用元编程魔法来替代FooBar?还是使用继承?对于视图,我是否应该只有产品视图,但使用巧妙的路由使其看起来像我有单独的(和 RESTful)/foos/bars 路径?

谢谢。

【问题讨论】:

  • 请说出 Foo 和 Bar 的区别。还请提供一个充分的理由为什么您需要两个模型而不是一个模型。谢谢。解决方案可能会根据您的情况而有所不同。
  • 不同之处在于它们生成序列号的方式,以及它们向用户注册的语义。

标签: ruby-on-rails model-view-controller dry


【解决方案1】:

对于控制器而言,继承将是一种不错的、干净且可维护的方式。在基本控制器中定义所有主要逻辑,并将对对象的任何调用替换为可以在继承控制器中覆盖的虚拟方法:

class Product < ApplicationController
  def resource
    nil
  end

  def new_resource
    nil
  end

  def do_something
    resource.do_something
  end

  def new
    @resource = new_resource
  end
end

class Foo < Product
  def resource
    @foo
  end

  def new_resource
    Foo.new
  end
end

继承性非常可维护,非常干净清晰,做这类工作的不错选择。

对于视图,您可以为所有常见的视图部分创建一个 shared 目录,这是另一种非常常见的方法,允许您以最小的麻烦完全 RESTful 控制 URL。如果你的对象也有类似的方法,那么视图不需要知道它正在处理什么类。

在上面的控制器中,访问 foos/new 的用户将启动对 new_resource 的调用,该调用将构建一个新的 Foo 实例并将其作为@resource 返回到页面。

#View:
<%= render 'shared/product_details', :locals => {:product => @resource} %>

【讨论】:

  • 我喜欢它的外观,除了 @ 变量。我仍然希望 @foo@bar 在视图中可用。如果这是 Python,我知道如何从子类中覆盖类变量访问,但是用 Ruby 来做这件事的研究…​​…令人沮丧。基本上我希望@foo 成为@product 的别名等等。我该怎么做?
  • 此答案中的示例显示了一种更通用的抽象控制器操作的方法:stackoverflow.com/a/6770284/562557
【解决方案2】:

如果您有完全相同的控制器方法和视图,您可以将其移至产品 - 这样,您将拥有 Product@product@products。由于是STIFooBar 上没有 ID 重复,因此成员路由也可以工作。此外,由于模型不同,鸭子类型会负责为每个模型调用正确的方法。

然后,您可以将两个子模型中未重复的所有内容移动到特定的控制器。

【讨论】:

    【解决方案3】:

    根据OP的进一步澄清,我现在有了自己的想法。

    模型继承适用于其他 OOP 语言,但在 Ruby 中不是必需的。在这种情况下,我认为最好的解决方案是使用 Module。

    您仍然需要两个模型 Foo 和 Bar,它们甚至可以具有不同的属性。

    然后,定义模型和控制器的常用方法

    # lib/product_model.rb
    module ProductModel
      def product_method_a
      end
    
      def product_method_b
      end
    end
    
    # lib/product_controller.rb
    module ProductController
      def show
        @obj = @model.constantize.find(params[:id])
        render 'products/show' # need to explicitly specify it because this is general 
      end
    
      def index
        @objs = @model.constantize.scoped
        render 'products/index' # need to explicitly specify it because this is general 
      end
    end
    

    然后,在 Foo 和 Bar 中使用这些方法,并根据需要添加自定义方法。

    class Foo < ActiveRecord::Base
      include ProductModel
    
      def serial_number
        # Foo's way
      end
    end
    
    class Bar < ActiveRecord::Base
      include ProductModel
    
      def serial_number
        # Bar's way
      end
    end
    

    对于控制器。

    class FoosController < ApplicationController
      @model = "foo"
      include ProductController
    end
    
    
    class BarsController < ApplicationController
      @model = "bar"
      include ProductController
    end
    

    对于视图,ProductController 已经定义了它们,只是为了在视图中使用通用匹配的变量名。

    <%= @obj.title %>
    

    很干燥,不是吗?

    【讨论】:

    • 我实际上曾经为模型做这个,后来改变了它。它似乎不太面向对象,而且我也喜欢使用 STI,我可以做到 Product.all 并以多态方式处理事物。
    • @tsm,OOP 不仅仅意味着“继承”。 Ruby 在这一点上更加灵活。 as_as_something 是 Rails 中非常流行的模式。 STI 有它的用例,但也有很多缺点,例如零散的空数据,多态中只有父级等。如果有的话,为什么不使用更好的替代方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多