2

我遇到了一种我不太确定如何建模的情况。

编辑:下面的代码现在代表一个可行的解决方案。不过,我仍然对更好看的解决方案感兴趣。

假设我有一个 User 类,并且一个用户有很多服务。然而,这些服务是完全不同的,例如 aMailService和 a BackupService,所以单表继承是行不通的。相反,我正在考虑将多态关联与抽象基类一起使用:

class User < ActiveRecord::Base
    has_many :services
end

class Service < ActiveRecord::Base
    validates_presence_of :user_id, :implementation_id, :implementation_type
    validates_uniqueness_of :user_id, :scope => :implementation_type

    belongs_to :user
    belongs_to :implementation, :polymorphic => true, :dependent => :destroy
    delegate :common_service_method, :name, :to => :implementation
end

#Base class for service implementations
    class ServiceImplementation < ActiveRecord::Base
    validates_presence_of :user_id, :on => :create

    #Virtual attribute, allows us to create service implementations in one step
    attr_accessor :user_id
    has_one :service, :as => :implementation

    after_create :create_service_record

    #Tell Rails this class does not use a table.
    def self.abstract_class?
        true
    end

    #Name of the service.
    def name
        self.class.name
    end

    #Returns the user this service
    #implementation belongs to.
    def user
        unless service.nil? 
            service.user
        else #Service not yet created
            @my_user ||= User.find(user_id) rescue nil
        end
    end

    #Sets the user this 
    #implementation belongs to.
    def user=(usr)
        @my_user = usr
        user_id = usr.id
    end

    protected

    #Sets up a service object after object creation.
    def create_service_record
        service = Service.new(:user_id => user_id)
        service.implementation = self
        service.save!
    end
end
class MailService < ServiceImplementation
   #validations, etc...
   def common_service_method
     puts "MailService implementation of common service method"
   end
end

#Example usage
MailService.create(..., :user => user)
BackupService.create(...., :user => user)

user.services.each do |s|
    puts "#{user.name} is using #{s.name}"
end #Daniel is using MailService, Daniel is using BackupService

请注意,我希望在创建新服务时隐式创建 Service 实例。

那么,这是最好的解决方案吗?或者甚至是一个好的?您是如何解决此类问题的?

4

2 回答 2

3

我认为您当前的解决方案行不通。如果 ServiceImplementation 是抽象的,那么关联的类将指向什么?如果 ServiceImplementation 没有将 pk 持久化到数据库中,has_one 的另一端如何工作?也许我错过了一些东西。

编辑:哎呀,我的原件也没有用。但这个想法仍然存在。代替模块,继续使用带有 STI 的服务而不是多态,并使用单独的实现对其进行扩展。我认为你被 STI 和不同实现中的一堆未使用的列所困扰,或者重新思考服务关系。您拥有的委派解决方案可能作为单独的 ActiveRecord 工作,但如果它必须具有 has_one 关系,我看不出它是如何作为抽象工作的。

编辑:那么,为什么不坚持委托而不是你原来的抽象解决方案呢?您必须为 MailServiceDelegate 和 BackupServiceDelegate 提供单独的表——如果您想避免使用纯 STI 的所有空列,不知道如何解决这个问题。您可以使用带有 delgate 类的模块来捕获常见的关系和验证等。抱歉,我花了几遍才赶上您的问题:

class User < ActiveRecord::Base
  has_many :services
end

class Service < ActiveRecord::Base
  validates_presence_of :user_id
  belongs_to :user
  belongs_to :service_delegate, :polymorphic => true
  delegate :common_service_method, :name, :to => :service_delegate
end

class MailServiceDelegate < ActiveRecord::Base
   include ServiceDelegate

   def name
     # implement
   end

   def common_service_method
      # implement
   end
end

class BackupServiceDelegate < ActiveRecord::Base
   include ServiceDelegate

   def name
     # implement
   end

   def common_service_method
      # implement
   end
end

module ServiceDelegate
   def self.included(base)
     base.has_one :service, :as => service_delegate
   end

   def name
     raise "Not Implemented"
   end

   def common_service_method
     raise "Not Implemented"
   end
end
于 2010-06-02T17:07:09.097 回答
0

我认为以下会起作用

在用户.rb

  has_many :mail_service, :class_name => 'Service'
  has_many :backup_service, :class_name => 'Service'

在服务.rb

  belongs_to :mail_user, :class_name => 'User', :foreign_key => 'user_id', :conditions=> is_mail=true
  belongs_to :backup_user, :class_name => 'User', :foreign_key => 'user_id', :conditions=> is_mail=false
于 2010-06-02T10:51:01.240 回答