第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Ruby 的類宏

在我們編寫 Ruby 的代碼時(shí),經(jīng)常會見到一些這樣像關(guān)鍵字的方法例如:attr_accessor,這種方法我們稱它為類宏(Class Macros),類宏是僅在定義類時(shí)使用的類方法。它們使我們可以跨類的共享代碼。本章節(jié)讓我們來深入了解一下它。

1. 創(chuàng)建一個(gè)類宏

讓我們在attr_accessor的基礎(chǔ)上來做一個(gè)新的類宏attr_checked,這個(gè)類宏可以為一個(gè)類賦予屬性,包括gettersetter方法,并可以通過Block來對屬性的值進(jìn)行校驗(yàn)。

具體表現(xiàn)形式為:

class Person
  include CheckedAttributes
  
  attr_checked(:age){|age| age >= 18}
  attr_checked(:sex){|sex| sex == 'man'}
  # ...
    
end

me = Person.new
me.age = 25
me.man = 'man'
puts me.age
puts me.man


# ---- 正常情況下的預(yù)期結(jié)果 ----
25
man

而當(dāng)我們不能通過校驗(yàn)的時(shí)候。

other = Person.new
other.age = 17      # 預(yù)期結(jié)果:拋出異常
other.sex = 'woman' # 預(yù)期結(jié)果:拋出異常

1.1 getter 和 setter 方法

讓我們從定義一個(gè)標(biāo)準(zhǔn)的agegettersetter方法開始。

實(shí)例:

class Person
  def age= age
    @age = age
  end
  
  def age
    @age
  end
end

me = Person.new
me.age = 18
puts me.age

# ---- 輸出結(jié)果 ----
18

1.2 使用eval來運(yùn)行多行代碼字符串

讓我們把定義class Person這部分當(dāng)做多行字符串,使用eval來執(zhí)行。

eval %Q{ 
  class Person
      def age= age
        @age = age
      end

      def age
        @age
      end
  end
}

me = Person.new
me.age = 18
puts me.age

# ---- 輸出結(jié)果 ----
18

1.3 動態(tài)賦予類屬性

類的補(bǔ)丁章節(jié)我們知道了重復(fù)定義類并不會創(chuàng)建同名的類,只會在其基礎(chǔ)上增加實(shí)例方法或類方法。所以我將剛剛定義類的字符串封裝成方法來為Person類添加屬性。

def add_checked_attribute(klass, attribute)
  eval %Q{ 
    class #{klass}
        def #{attribute}= #{attribute}
          @#{attribute}= #{attribute}
        end

        def #{attribute}
          @#{attribute}
        end
    end
  }
end

add_checked_attribute(:Person, :age)
add_checked_attribute(:Person, :sex)


me = Person.new
me.age = 18
me.sex = 'man'
puts me.age
puts me.sex

# ---- 輸出結(jié)果 ----
18
man

1.4 去掉eval,重構(gòu)方法

使用eval有時(shí)候并不是一個(gè)好辦法,會影響整體代碼的可讀性和維護(hù)性,因此我們使用class_eval以及實(shí)例變量setget方法來實(shí)現(xiàn)這個(gè)方法。

class Person
end

def add_checked_attribute(klass, attribute)
  klass.class_eval do
    define_method "#{attribute}=" do |value|
      instance_variable_set("@#{attribute}", value)
    end
    
    define_method attribute do 
      instance_variable_get "@#{attribute}"
    end
  end
end

add_checked_attribute(Person, :age)
add_checked_attribute(Person, :sex)


me = Person.new
me.age = 18
me.sex = 'man'
puts me.age
puts me.sex

# ---- 輸出結(jié)果 ----
18
man

注意事項(xiàng):這時(shí)因?yàn)槲覀儸F(xiàn)在不定義Person類,所以需要在最前面先定義一個(gè)Person類,否則Ruby會因?yàn)闊o法找到Person類而報(bào)錯(cuò)。

1.5 增加校驗(yàn)屬性的Block

讓方法對傳入的Block值進(jìn)行校驗(yàn)

class Person
end

def add_checked_attribute(klass, attribute, &validation)
  klass.class_eval do
    define_method "#{attribute}=" do |value|
      raise 'Invalid attribute!' unless validation.call(value)
      instance_variable_set("@#{attribute}", value)
    end
    
    define_method attribute do 
      instance_variable_get "@#{attribute}"
    end
  end
end

add_checked_attribute(Person, :age) {|age| age >= 18}
add_checked_attribute(Person, :sex) {|age| age == 'man'}


me = Person.new
me.age = 18
me.sex = 'man'
puts me.age
puts me.sex

# ---- 輸出結(jié)果 ----
18
man

當(dāng)我們賦予屬性的值不滿足條件的時(shí)候會拋出異常。

me = Person.new
me.sex = 'woman'

# ---- 輸出結(jié)果 ----
Invalid attribute! (RuntimeError)

1.6 最后將方法定義到模塊,完成類宏

我們在引入類宏的模塊的時(shí)候使用的是include,所以我們使用included鉤子方法,在鉤子方法對引用的類進(jìn)行extend(因?yàn)?code>extend模塊添加類方法),替代之前的class_eval,將之前定義屬性的方法定義到被extend的模塊中,從而使定義的方法可以被類調(diào)用(類方法)。

# 定義模塊部分
module CheckedAttributes
  def self.included(klass)
    klass.extend ClassMethods
  end
end

module ClassMethods
  def attr_checked(attribute, &validation)
    define_method "#{attribute}=" do |value|
      raise 'Invalid attribute!' unless validation.call(value)
      instance_variable_set("@#{attribute}", value)
    end
    
    define_method attribute do 
      instance_variable_get "@#{attribute}"
    end
  end
end

# 引用部分
class Person
  include CheckedAttributes
  
  attr_checked :age {|age| age >= 18}
  attr_checked :sex {|sex| sex == 'man'}
end

me = Person.new
me.age = 18
me.sex = 'man'
puts me.age
puts me.sex

# ---- 輸出結(jié)果 ----
18
man

當(dāng)我們賦予屬性的值不滿足條件的時(shí)候同樣會拋出異常。

me = Person.new
me.age = 10

# ---- 輸出結(jié)果 ----
Invalid attribute! (RuntimeError)

2. 小結(jié)

在本章節(jié)中,我們一步一步創(chuàng)建一了個(gè)類宏。宏在今后的開發(fā)中會為您省去大量的時(shí)間,大量降低維護(hù)成本和溝通成本。