天天看點

Ruby 基礎+進階 第二天

本次學習資料

通讀《Ruby元程式設計》,記錄下有趣的以及不明白的知識點

元程式設計

元程式設計是能編寫能寫代碼的代碼。

編寫在運作時操作自身的代碼,稱為動态元程式設計,代碼生成器和編譯器的那種方式稱為靜态元程式設計。Ruby不需要編譯,是以非常适合元程式設計。

内省機制

inspection,意思為檢查,檢討的意思,是以顧名思義,就是可以檢測自己屬于什麼類,自己有沒有某個方法,自己的父類是誰。

◆:一個ruby對象o 的内省

1:得到自己的object id: o.object_id

2:檢測自己的類型 inspect type: [].class == Array

3:檢測自己的類:inspect class: o.class == Foo o.instance_of?(Foo)

4:檢測類的繼承:inspect class hierarchy: o.class.superclass o.class.included_modules

5:檢測自己是否擁有某個方法: o.respond_to?(“reverse”)

6:檢測自己有哪些執行個體方法(false表示隻要類自己的方法,不要繼承的):o.class.instance_methods(false)

7:檢測自己有哪些執行個體變量:o.class.instance_variables

tips:Active Record通過内省機制檢視類的名字,根據類名自動的映射到對應的表(類名:駝峰命名法,表名:小寫,以“_”分割),然後讀取表中字段,自動定義同名的屬性和相應的通路器。

打開類、猴子更新檔、細化(了解)

Ruby的class關鍵字更像是一個作用域操作符,而不是類型聲明語句。class關鍵字的核心任務是把你帶到類的上下文中,讓你可以在裡面定義方法。

在Ruby中,定義一個未存在的類可以定義這個類,若定義一個存在的類,就不再定義,會打開這個類,進行這次定義中的操作。比如,可以定義String類來增加新的方法。

class String
  def to_asd
    "asd"
  end
end
a = "qwe"
puts a.to_asd
#=>asd
           

但若是String本身就有to_asd方法,則會覆寫掉原本的to_asd方法,導緻Bug。是以這種魯莽的修改類的方式叫做猴子更新檔。

為了解決猴子更新檔問題,需要用到細化refine,細化和打開類的差別在于細化不是全局性的,細化就像在原來的類上加了一塊更新檔,而且它會覆寫正常的方法查找,此外,細化隻在程式的部分區域生效,從using語句的位置開始到子產品或檔案結束。

module StringExtensions
  refine String do
    def reverse
      "esrever"
    end
  end
end

module StringStuff
  using StringExtensions
  puts "my_string".reverse
end
puts "my_string".reverse
#=>esrever
#=>gnirts_ym

           

類似的,細化也有陷阱,比如:

class MyClass
  def my_method
    puts "original"
  end
  def another_method
    my_method
  end
end

module MyClassRefinement
  refine MyClass do
    def my_method
      puts "refined"
    end
  end
end

using MyClassRefinement
MyClass.new.my_method
MyClass.new.another_method
#=>refined
#=>original
           

可以看出盡管調用了using,another_method仍然調用的using前的my_method。

類與子產品

子產品基本上就是一堆方法組成的包。

每個類都是一個子產品,類就是帶有三個方法(new,allocate,superclass)的增強子產品,通過這三個方法可以組織類的繼承結構,并建立對象。

Ruby中的類和子產品的概念十分接近,完全可以将二者互相替代,之是以同時保留二者的原因是為了保持代碼的清晰性,讓代碼意圖更加明确。使用原則:

1.希望把自己代碼包含(include)到别的代碼中,應該使用子產品

2.希望某段代碼被執行個體化或被繼承,應該使用類

子產品機制可以用來實作類似其它語言中的命名空間(Namespace)概念

類與對象

類就是一個對象(Class類的一個執行個體)外加一組執行個體方法和一個對其超類的引用。Class類是Module類的子類,是以一個類也是一個子產品。

對象就是一組執行個體變量外加一個指向其類的引用。對象的方法并不存在于對象本身,而是存在于對象的類中。

對象與類與子產品關系圖

Ruby 基礎+進階 第二天

常量與命名空間與 ::

Ruby中任何以大寫字母開頭的引用都是常量,常量的路徑(作用域),類似與檔案系統中的目錄,通過::進行分割和通路,預設直接以::開頭(例: :: Y)表示變量路徑的根位置

Y = "outside"
module Constant
  Y = "inside"
  class Inside
    Y = "in inside"
  end
end
puts Constant::Inside::Y
puts Constant::Y
puts ::Y
#=> in inside
#=> inside
#=> outside
           

Include、Extend、 Load、Require 的使用差別

1.include和extend用于在類中引入子產品,差別:

include:把子產品中的方法作為類的執行個體方法

extend: 把子產品中的方法作為類的類方法

module A
	def class_type
		puts "This class is of type:#{self.class}"
	end
end
class B extend A end
class C include A end
B.class_type
C.new.class_type
           

2.require、load用于加載庫,如.rb檔案,差別:

require: 加載一個庫,并且隻加載一次,如果多次加載會傳回false。檔案不需要擴充名。一般用于導入類庫。

load: 加載一個庫,可多次加載。需要指定檔案擴充名。一般用于加載代碼。load可以通過load(‘xxx.rb’,true)來讓xxx.rb中的常量僅在自身範圍内有效。

#在目前目錄中有一個 two.rb檔案
require './two';
load './two.rb';
           

接收者與祖先鍊與prepend、include

接收者就是調用方法所在的對象,比如,my_string.reverse()中,my_string就是接收者。

一個類找到它的超類,然後再找到超類的超類,以此類推直到找到BasicObject類,這個過程中經曆的類路徑就是該類的祖先鍊。

因為類與子產品是父子關系,是以祖先鍊中也可以包含子產品,prepend與include分别可以向鍊中添加子產品,不同的是調用include方法,子產品會被插入祖先鍊,目前類的正上方,而prepend同樣是插入到祖先鍊,但位置其他卻在目前類的正下方,另外通過Class.ancestors可以檢視目前的祖先鍊。

module M1
  def my_methods
    'M1#my_methods()'
  end
end

class C
  include M1
end

class D < C; end

puts D.ancestors

#=> D
#=> C
#=> M1
#=> Object
#=> Kernel
#=> BasicObject
           

tips: 如果多次引用同一個子產品,ruby會忽略掉這個引用指令。

self

1.調用一個方法時,接收者會扮演self角色

2.定義一個子產品(或類)時,該子產品(或類)會扮演self角色

3.執行個體變量永遠被認定為self的執行個體變量

4.任何沒有明确指定接收者的方法調用,都當做是調用self的方法

class MyClass
  def testing_self
    @var = 10
    my_method()
    puts self
    puts "#{@var}"
  end
  def my_method
    @var = @var + 1
  end
end

obj = MyClass.new
obj.testing_self

#=>#<MyClass:0x00007f9353874680>
#=>11
           

private

不能通過明确指定接受者來調用私有方法。私有方法隻能通過隐性的接受者self調用.

繼續閱讀