禮貌性實作

2004 年 8 月 12 日

當你撰寫類別時,你通常會努力確保該類別的功能對該類別有意義。但有時,新增一個功能讓類別符合它自然應該具備的更豐富介面是有意義的。

最常見且顯而易見的範例就是使用複合模式時會遇到的範例。讓我們來考慮一個簡單的容器範例。你有一些盒子,可以裝其他盒子和大象(這是虛擬大象的優點)。你想知道一個盒子裡有多少大象,考慮到你需要計算盒子裡、盒子裡的盒子裡、盒子裡的盒子裡的大象。當然,解決方案就是一個簡單的遞迴。

# Ruby
class Node
end

class Box < Node
  def initialize 
    @children = []
  end
  def << aNode
    @children << aNode
  end
  def num_elephants
    result = 0
    @children.each do |c|
      if c.kind_of? Elephant
        result += 1
      else
        result += c.num_elephants
      end
    end
    return result
  end
end

class Elephant < Node
end

現在,kind_of? 測試在 num_elephants 中是一個警訊,因為我們應該小心任何測試物件類型的條件。另一方面,還有其他選擇嗎?畢竟我們會進行測試,因為大象無法包含盒子或大象,所以詢問牠們裡面有多少大象沒有意義。詢問大象牠們包含多少大象不符合我們的世界模型,因為牠們無法包含任何東西。我們可能會說這沒有模擬真實世界,但我的範例對這個論點來說感覺有點太異想天開了。

然而,當人們使用複合模式時,他們通常會提供一個方法來避免條件 - 換句話說,他們會這樣做。

class Node
  #if this is a strongly typed language I define an abstract
  #num_elephants here
end

class Box < Node
  def initialize 
    @children = []
  end
  def << aNode
      @children << aNode
  end
  def num_elephants
    result = 0
    @children.each do |c|
      result += c.num_elephants
    end
    return result
  end
end

class Elephant < Node
  def num_elephants
    return 1
  end
end

許多人對這種事情感到非常不安,但它確實可以極大地簡化掃描複合結構的程式碼邏輯。我把它想成讓葉類別(大象)提供一個簡單的實作,作為它在階層中作為節點的角色的禮貌。

我喜歡畫的類比是數學中將數字提升到 0 次方的定義。定義是任何數字提升到 0 次方都是 1。但直覺上,我不認為說任何數字乘以它自己 0 次是 1 有道理 - 為什麼不是零?但這個定義讓所有數學運算順利進行 - 所以我們暫停我們的懷疑並遵循定義。

每當我們建立模型時,我們都在設計一個模型來適應我們想要感知世界的方式。如果禮貌性實作簡化了我們的模型,那麼它們就是有價值的。

於 2014 年 8 月 27 日重新發布