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