清单 10. 实现继承
create_table "people" do |t| t.column "type", :string t.column "first_name", :string t.column "last_name", :string t.column "loyalty_number", :string end class Person < ActiveRecord::Base end class Customer < Person has_many :orders end
在很多方面,这种解决方案都可以很好地工作。代码简单且无重复性。这些查询简单且性能很好,因为您无需进行任何连接来访问多个子类,ActiveRecord 可以使用 type 列决定哪个记录能够返回。
在某些方面,ActiveRecord 继承十分有限。如果已有的继承等级非常宽,继承就会失效。例如,在 ChangingThePresent,内容有很多类型,每种类型都有自己的名称、或短或长的描述、某些常见的表示属性以及几个定制属性。我们很希望 cause、nonprofit、gift、member、drive、registry 以及其他一些类型的对象都能够从通用的基类中继承,以便我们能以同样的方式处理所有类型的内容。但我们却不能如此,因为 Rails 模型将会在单一表中拥有我们所有对象模型的实质内容,这不是一个可行的解决方案。
探索其他可选方案
我们针对此问题试验了三种解决方案。第一,我们在类自身的表中放置每个类,使用视图为内容构建通用表。我们很快抛弃了此种解决方案,因为 Rails 不能很好地处理数据库视图。
我们的第二个解决方案是使用简单的多态。通过这种策略,每个子类都会拥有其自身的表。我们将通用列推入每个表。例如,比方说我需要一个名为 Content 的子类,它只包含 name 属性,以及 Gift、Cause 和 Nonprofit 子类。Gift、Nonprofit 和 Cause 都可有 name 属性。由于 Ruby 是动态类型的,所以这些子类无需从通用基类中继承。它们只需对相同的一组方法进行响应。ChangingThePresent 在几个地方使用了多态以提供通用的行为,尤其是在处理图像的时候。
第三种方法是提供一种通用的功能,但采用的是关联而非继承。ActiveRecord 具有一种称为多态关联的特性,非常适合将通用行为附加给类,完全无需继承。在之前的 Address,您已经看到了多态关联的示例。我可以使用相同的技术(而非继承)附加通用属性用于内容管理。考虑名为 ContentBase 的类。通常,为了将该类关联到另一个类,可以使用 has_one 关系和一个简单的外键。但您可能更想让 ContentBase 能与多个类共同工作。这时,您需要一个外键,还需要一个能定义目标类的类型的列。而这恰好是 ActiveRecord 多态关联所擅长的方面。请参看清单 11。
清单 11. 站点内容关系的两个方面
class Cause < ActiveRecord::Base has_one :content_base, :as => :displayable, :dependent => :destroy ... end class Nonprofit < ActiveRecord::Base has_one :content_base, :as => :displayable, :dependent => :destroy ... end class ContentBase < ActiveRecord::Base belongs_to :displayable, :polymorphic => true end










