over 3 years ago

兩個Model如果是繼承關係,可以用子類別可以繼承父類別的方法或特性,如果兩個Model沒有繼承關係,想要讓一個類別使用另一個類別的方法,則可以用delegate。

本文翻譯自此https://simonecarletti.com/blog/2009/12/inside-ruby-on-rails-delegate/
翻譯是為方便自己理解,建議直接看原文~

Delegation 是一個Ruby專案中常見的例子,如果你把proxies, mixins 和 composition 當成一種delegation模式的話。

Delegation 設計模式是讓物件或實例曝露一些性質,但這些性質其實是取用與當前實例相關連的其它實例的性質而來的。

常見技巧,當呼叫一個不存在的方法時會得到method_missing,然後回傳該呼叫到正確的handler。然而,這不總是好辦法。在Ruby中還有更好方法去做delegation。

Ruby標準函式庫中包含 Delegate module,為Delegation提供支援。不幸的,我發現它比傳統的做法更複雜,因此我從未用過它。

ActiveSupport Delegate module

如果你的專案有ActiveSupport你可以有更簡便的方式做出delegation:Module#delegate擴充。你可以使用它提供的delegate 模組在你的類別或模組裡,可以delegate一個方法給一個關連的實例。

舉例來說,考慮Post belongs_to a User.

class Post < ActiveRecord::Base
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :posts
end

你也許想要呼叫post.name,並讓它回傳user.name,該user為post的關連實例。通常來說,你須要創建一個name方法。如下所示:

class Post
  belongs_to :user

  def name
    # let's use try to bypass nil-check

    user.try(:name)
  end
end

相同的程式可以用delegate方法表示。惟須注意,delegate的方法要是public。

class Post
  belongs_to :user
  delegate :name, :to => :user, :allow_nil => true
end

delegate方法可以被使用在任何情境,不只侷限於ActiveRecord models。舉例來說,你的 queue wrapper 可以 delegate 到內部的 queue implementation 的特定方法。

class QueueManager

  attr_accessor :queue

  # Delegates some methods to the internal queue

  delegate :size, :clear, :to => :queue

  def initialize
    self.queue = []
  end

end

m = QueueManager.new
m.size
# => 0

m.clear
# => []

方法可以被 delegated 到實例變數,類別變數或常數(使用symbol)。:to 之後至少須要接一個方法。

Options

delegate 方法可以加入一些選項來客製特性。

這是我最愛的,:prefix可以設定為true使得關聯的實例會出現在方法名稱中。你也可以自行設定prefix。

class Post
  belongs_to :user

  delegate :name, :to => :user, :prefix => true
  # post.user_name


  delegate :name, :to => :user, :prefix => "author"
  # post.author_name

end

:allow_nil選項允許類別delegate該方法到一個nil實例中。在此情況下,該方法會回傳nil。預設行為是發出NoMethodError

class Post
  belongs_to :user
  delegate :name, :to => :user, :prefix => true
end

Post.new.user_name
# raise NoMethodError


class Post
  belongs_to :user
  delegate :name, :to => :user, :prefix => true, :allow_nil => true
end

Post.new.user_name
# => nil

:to不是選項,是強制須要輸入的東西。

Documentation

作者建議直接看source code

← Raspberry Pi 使用ssh登入 Git 會前準備 →
 
comments powered by Disqus