处理两个类似对象的最佳实践

时间:2010-02-07 01:53:04

标签: ruby-on-rails ruby

假设我有两个基本相同的控制器/视图集。

营业时间和工作时间

第一个包含business_id,后者包含staff_id。 否则他们看起来一样。 id,mon_start_time,mon_stop_time,tues_start_time,tues_stop_time等......

1) 有没有办法我会使用相同的控制器,因为它们是如此相似?它似乎没有多大意义,但我只是在考虑它们有多相似以及有多少重复代码。

2) 另外,对于每一个我都有部分表格。我正在尝试在工作时间和工作时间使用相同的工作时间。处于最简化状态的部分状态如下:

  

-form_for(obj)do | f |
      = f.error_messages        %p       = select obj.class,:mon_start_time,hours       至       = select obj.class,:mon_stop_time,hours

     

= link_to'Back',obj_path

所以我需要传递3个独特的东西.'obj',无论是business_hours还是staff_hours。这在form_for中没问题,但是如何获取选择的第一个参数的小写控制器名称?我需要来自'obj'的'business_hour'和'staff_hour'。 然后我需要它知道正确的“后退”链接。

我知道我可以将参数传递给部分,但我只是好奇是否有一种更流畅的方式来解决这个问题。 谢谢!

4 个答案:

答案 0 :(得分:2)

重复代码具有持有成本,维护成本。在我们重构重复代码并发现自己松了一口气之前,我们有时不知道成本有多高:现在我可以在一个地方改变业务规则。现在我可以停止输入两次了。

您可以使用两个控制器,但仍然可以重构重复的代码。一种方法是将公共代码放在两个控制器包含的模块中。

module CommonStuff

  def stuff_that_is_the_same
  end

end

controller FooController < ApplicationController

  include CommonStuff

  def stuff_that_is_different
    # Stuff specific to Foo
    stuff_that_is_the_same
    # More stuff specific to Foo
  end

end

controller BarController < ApplicationController

  include CommonStuff

  def stuff_that_is_different
    # Stuff specific to Bar
    stuff_that_is_the_same
    # More stuff specific to Bar
  end

end

答案 1 :(得分:2)

就获取控制器的名称而言,您可以通过调用方法

在任何控制器操作中获取它
 controller_name

并且@controller实例变量中的控制器状态可用于您的视图,因此如果您想在视图中访问它,那么您可以

 @controller.controller_name

现在看看你的BusinessHours和StaffHours课程,我会说最好的办法是让它们变成多态。你在这里要做的第一件事是摆脱几乎相同的表。请查看rails core polymorphic docs

注意:但@amurmann提到的has_many_polymorphs尚未在rails core中提供,但您可以将其用作插件。 Pratik写了一篇关于它的博客文章here

要从控制器中删除重复的代码,您可以将其放在模块中(如@Wayne所说)或创建一个基本控制器,您的Business和Staff小时控制器将从中继承所有常用功能。现在解决方案完全取决于您的应用程序更有意义。就个人而言,我将创建一个基本控制器,因为它更多OO,保持我的类结构化,代码不会隐藏在某些模块中。但有些人可能不这么认为。

答案 2 :(得分:0)

听起来我可能会将两者合并为一种资源。在本次讨论中,我们只需将资源称为“小时”。 “小时”可能属于企业或员工的问题可能通过使关系变为多态来解决。

您可以使用has_many_polymorphs确定解决该关系问题:

class Hours < ActiveRecord::Base
  has_many_polymorphs :participants, :from => [:staff, :business], :through => :hours_participants
end

这需要一个新表,可能不是具有最佳性能的解决方案。但是,它应该是一个干燥的解决方案。

答案 3 :(得分:0)

你有没有看过Single Table Inheritance?我认为这对你来说是最好的解决方案。

基本上,您定义了一个小时表和模型,它看起来完全像员工小时或营业时间,并包含一个名为type的字符串列。然后,您创建小时的子类,以分别定义特定于业务和员工小时的方法和验证。您也可能希望将关联重新定义为更通用的关联。在这个例子中,我称之为守护者,就像那个保持这些时间的人一样。

简而言之,rails正在为您处理所有多态行为。

型号:

class Hour < ActiveRecord::Base
  # common associations/methods etc go here
end

class BusinessHour < Hour
  belongs_to :keeper, :class_name => "Business"
  #business hour specific logic 
end

class StaffHour < Hour
  belongs_to :keeper, :class_name => "Staff"
  #staff hour specific logic
end

class Business < ActiveRecord::Base
  has_many :business_hours, :foreign_key => :keeper_id
  ...
end

class Staff < ActiveRecord::Base
  has_many :staff_hours, :foreign_key => :keeper_id
  ...
end

路线:

map.resources :business_hours, :controller => "hours", :class => "BusinessHour"
map.resources :staff_hours, :controller => "hours", :class => "StaffHour"

控制器:

class HoursController < ApplicationController
  before_filter :select_class
  def select_class
    @class = Kernel.const_get(params[:class])
  end

  def new
    @hour = @class.new
    ...
  end

  def show 
    @hour = @class.find(params[:id])
    ...
  end

  ...
end

视图看起来几乎就是你现在所拥有的,但你会想要一些助手来保持输出的直接。

module HourHelper
  def keeper_type
    @class == "BusinessHours" ? "Business" : "Staff"
  end     
end




-form_for(obj) do |f|
  =f.error_messages 
  %p =select keeper_type, :mon_start_time, hours 
    to 
    =select keeper_type, :mon_stop_time, hours

=link_to 'Back', obj_path

您可能还需要考虑创建自定义表单构建器以简化表单创建。