Rails提供了两种机制,能够将复杂的面向对象模型映射为关系模型,即所谓的单表继承(single-table inheritance)和多态关联(polymorphic associations,也有人称为多表继承)。数据库
在使用面向对象开发时,常常会用到类和继承,如应用程序中涉及不一样角色的人员(People):顾客(Customer)、员工(Employee)和经理(Manager)等等。其中有一些属性是共有的,另外一些属性是特有的。所以,建立模型:Customer类和Employee类,且都是People的子类,Manager类是Employee的子类。子类会继承父类的全部属性和行为。ruby
这些都是代码中的模型体现,在关系型数据库是如何体现的?见以下代码:架构
# model的定义 class People < ActiveRecord::Base end class Customer < People end class Employee < People belongs_to :boss, class_name: 'Employee', foreign_key: 'reports_to' end class Manager < Employee end
# 迁移文件的定义,即关系型数据库的体现 create_table :people, :force => true do |t| t.column :type, :string # common attributes t.column :name, :string t.column :email, :string # attributes for type=Customer t.column :balance, :decimal, :precision => 10, :scale => 2 # attributes for type=Employee t.column :reports_to, :integer t.column :dept, :integer # attributes for type=Manager # none end
# 添加人员 boss = Manager.create({name: 'Jobs', email: 'jobs@apple.com', dept: 1}) empl = Employee.new({name: 'David', email: 'david@apple.com', dept: 2}) empl.boss = boss empl.save user = Customer.create({name: 'Jim', email: 'jim@rails.com', balance: 100})
由上面的代码能够看出类定义中使用了继承,而对于不一样角色的人员,均保存于people表,对于boss和empl对象,都是没有user的balance属性的,即这两个对象的balance字段都是null,而对于user对象的dept和reports_to字段一样是null,这样就实现了一个单表继承,相比于咱们使用各类其它方法会简单不少。可是问题是:app
经过迁移文件中能够看到,在people表中添加了type字段。经过上图也能够看到,每条记录的type字段都有值,并且为用户的角色。ActiveRecord是约定好了使用type字段来描述对象所属的类型。学习
针对此问题,能够详见Martin Fowler在《企业应用架构模式》的介绍。测试
是的,也许一个新加入项目组的同窗使用People添加了人员,即便是测试,也会感受到代码不是那么严谨,例如:code
god = Person.create({name: 'God', email: 'god@god.god'})
发生了什么?God真的是神啊,type为空,因此,咱们必定不想在表中见到神,按以下三种方法都可以解决:对象
- 在People中实现一个名为abstract_class?的类方法,并使其返回true,这样,就能够达到目的了。不过它带来的问题是:1.ActiveRecord永远不会尝试寻找对应于抽象类的数据库表,这是对咱们有利的,2.抽象类的子类会被看成各自独立的ActiveRecord模型类,即各自映射到一张独立的数据库表。这就达不到咱们对公共属性抽取的目的了。这种方法不完美
- 使用Ruby模块来包含这些须要共享的功能,而后将模块混入所谓的子类。这是书中提供的方法,也感受不完美,没有了继承的感受
- 可否在父类中添加什么使其只可读不可写呢?
单表继承中全部的属性都存在于一张表中,这样真的好吗?若是子类存在的差别较大,且属性数据较大,若是仍然存在于同一张表,就会产生不少问题。这时能够学习多态继承了。blog