礼貌实现

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 日重新发布