些細な処理をするラッパーオブジェクトをどんどん作るとオブジェクト指向が上達する、ような気がする

ThoughtWorksアンソロジー ―アジャイルとオブジェクト指向によるソフトウェアイノベーション の第5章に「オブジェクト指向エクササイズ」というのがある。以下のページを参考にさせていただいているうち、なんとなくオブジェクト指向でプログラムを作るということがわかってきた。

9つのルールのうち、以下の2つに注目する。

  • ルール3:すべてのプリミティブ型と文字列型をラップすること
  • ルール8:ファーストクラスコレクションを使用すること

ここから見えてくるものは「既存部品を基に自分用の部品を作ってから使え」ということだ。既存の部品(クラス)はそのまま使うのではなく、それをラップする新しいクラスを作成して使う。すると、そのクラスの内部表現(BooleanかIntか等)が変わったときにも、それを利用する側は何も変更しなくてよい。関連する処理をそのクラスに入れればすっきりするし、責務の割り当ての問題も自然にクリアできる。

この考え方を進め、ある値に特定の処理をする場合には、その値をラップした新しいクラスを作成し、処理をそのクラスに入れることにする。たとえば、あるオブジェクトaに関する処理をする関数fを作るならば、fを新しいクラスCのメソッドにする。つまり

def f(a)
  # 処理
end

def b(a)
  f(a)
end

このような処理があったら

class C
  def initialize(a)
    @a = a
  end
  
  def f
    # 処理
  end
end

def b(a)
  c = C.new(a)
  c.f
end

こう変えてみる(サンプルコードはRuby)。インスタンス変数はラップする値を保持する程度でよいので、ルール7の「1つのクラスにつきインスタンス変数は2つまでにすること」もクリアしやすくなる。

1行に複数のドットが必要な処理(戻ってきたオブジェクトのメソッドを呼び出すような処理)があったときにも、ラッパークラスのメソッドに入れてしまうことで減らすことができる。たとえば

name = order.customer.name

という処理は、orderをラップするOrderCustomerクラスを作成し

c = OrderCustomer.new(order)
name = c.name

のように書き直すことができる。これにより、ルール4の「1行につきドットは1つまでにすること」もクリアしやすくなる。もしorder.customer.nameがorder.customer.fullnameに変更された場合でも、OrderCustomerの内部処理を修正すれば利用する側のコードを修正する必要がないため、メンテナンス性が向上する。

このように些細な処理をするオブジェクトをどんどん生成しては破棄していくようなスタイルのプログラミングがオブジェクト指向で上達するためのポイントではないかと最近思っている。