Ruby类继承、抽象类、类拓展混入、代理类实例
总结一下工作中遇到的类扩展:
1、类继承:
当多个类公用很多方法的时候可以将公用方法部分抽取出来,需要的类做相关继承。
例子:
class a < activerecord::base
def a
p "it was a "
end
end
class b<a
end
class c<a
end
b.new.a #=>"it was a "
c.new.a #=>"it was a "
2、抽象类
当多个类要继承一个类时,用第一种方法,会遇到一个问题。
(引用一个别人的注解来描述抽象类的运用吧https://ihower.tw/rails4/activerecord-others.html)
單一表格繼承sti(single-table inheritance)
如何將物件導向中的繼承概念,對應到關聯式資料庫的設計,是個大哉問。rails內建了其中最簡單的一個解法,只用一個資料表儲存繼承體系中的物件,搭配一個type欄位用來指名這筆資料的類別名稱。
要開啟sti功能,依照慣例只要有一個欄位叫做type,型態字串即可。假設以下的posts資料表有欄位叫做type,那麼這三個models實際上就會共用posts一個資料表,當然,還有這兩個子類別也都繼承到父類別的validates_presence_of :subject:
class post < activerecord::base
validates_presence_of :subject
end
class guestpost < post
end
class memberpost < post
end
讓我們進入rails console實驗看看,rails會根據你使用的類別,自動去設定type欄位:
post = guestpost.create( :subject => "guest")
post.type # "guestpost"
post.id # 1
post = memberpost.create( :subject => "member" )
post.id # 2
post.type # "memberpost"
guestpost.last # 1
很遺憾,也因為這個慣例的關係,你不能將type這麼名字挪做它用。
sti最大的問題在於欄位的浪費,如果繼承體系中交集的欄位不多,那麼使用sti就會非常的浪費空間。如果有較多的不共用的欄位,筆者會建議不要使用這個功能,讓個別的類別有自己的資料表。要關閉sti,請父類別加上self.abstract_class = true
class post < activerecord::base
self.abstract_class = true
end
class guestpost < post
end
class memberpost < post
end
這裡的guestpost和memberpost就需要有自己的migrations建立guest_posts和member_posts資料表。
你还可以在某个类中,引入多个依赖
class dependency<post
require_dependency 'guestpost'
require_dependency 'memberpost'
end
3、类拓展混入
ruby的类是单继承的,要实现多继承的功能需要用mixin(参合模式)的方式,即类拓展混入来实现。例子:
module extract
def self.included(base)
base.extend(classmethods)
end
module classmethods
def a
p "it was a "
end
end
end
class a<activerecord::base
include extract
end
a.new.a #=>"it was a"
4、代理类
当某个功能是比较复杂的,当然写再lib中,作为一个面向函数的方法去处理很简单,也可以用代理类的方式实现面向对象的调用。
例子:
class a<activerecord::base
def generate_schedule
generator = generator::excelgenerator.new(self)
generator.generate_schedule
end
end
module generator
class excelgenerator
attr_reader :excel,:workbook,:a,:worksheet
attr_accessor :styles
def initialize(a)
@excel ||= axlsx::package.new
@workbook ||= @excel.workbook
@worksheet = @workbook.add_worksheet(:name => '测试生成一个excel文件')
@a ||= a
@styles ||= hash.new
end
def generate_schedule
#excel内容的具体定义
end
end
end
a.new.generate_schedule 就可以通过代理类excelgenerator实现一个a的类实例方法generate_schedule
当然也可以通过include 一个model的方式实现添加类实例方法,有时候也可以混合使用。另外使用代理类的好处在于多个类都需要相同方法的时候可以定义共同的部分,举一个发邮件的例子:
class a<activerecord::base
include sendemail
end
class b<activerecord::base
include sendemail
end
实现引入模块:
module sendemail
#include this module to class::a and b
#use like that-- a.first.send_email
def send_email
email.call_email(self)
end
end
实现代理类:
class email < actionmailer::base
default :from => "test@email.com"
def self.call_email(obj)
define_method "#{obj.state}" do |obj|
@obj = obj
mail(:to => @obj.email, :subject => "xx标题" )
end
send("#{obj.state}").deliver
#根据不同对象obj.state得到不同状态下,定义不同方法,然后send派发调用相关对象状态的模板。
end
end
ruby很灵活当然还有很多其他的方法实现更多的方式,以后再慢慢总结。