以下の内容はhttps://kamatimaru.hatenablog.com/entry/2026/01/20/001054より取得しました。


「委譲」の適切な使い方を『リファクタリング』を読んで学ぶ

最近、オブジェクト指向における「委譲」の適切な使い方について考える機会があったのでマーティン・ファウラーの『リファクタリング』を読みながら勉強してみます。サンプルコードはRubyです。

www.ohmsha.co.jp

サンプルコード

以下のようにNameクラスとPersonクラスがあるとします。PersonクラスはNameクラスに依存しています。

PersonクラスのコンストラクタにNameクラスのインスタンスを渡す方法もあり、そこでも議論ができそうですが本記事では割愛します。

Nameクラス

class Name
  attr_reader :first_name, :last_name

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end

  def full_name
    "#{@last_name} #{@first_name}"
  end
end

Personクラス

class Person
  def initialize(first_name, last_name)
    @name = Name.new(first_name, last_name)
  end
end

PersonクラスのインスタンスからNameクラスのfull_nameメソッドを呼ぶ方法を考えます。以下の2パターンがありそうです。

1.Personクラスで委譲メソッドを提供する

1つ目は以下のようにPersonクラスのインスタンスから直接full_nameメソッドを呼ぶ方法です。

person = Person.new("太郎", "山田")
puts person.full_name # 山田 太郎

このためにはPersonクラスでName#full_nameメソッドに委譲するメソッドを提供する必要があります。

class Person
  # 省略
  def full_name
    @name.full_name
  end
end

2.nameフィールド経由で呼ぶ

2つ目は以下のようにPersonnameフィールド(=Nameクラスのインスタンス)を経由してfull_nameメソッドを呼ぶ方法です。

person = Person.new("太郎", "山田")
puts person.name.full_name # 山田 太郎

そのためにはPersonクラスにattr_readerを定義してnameフィールドを公開する必要があります。

class Person
  attr_reader :name
  # 省略
end

メリット/デメリット

それぞれのパターンのメリット/デメリットについて考えます。マーティン・ファウラーの『リファクタリング』に詳しく解説されています。

  1. 委譲の隠蔽(p.196)
  2. 仲介人の除去(p.199)

です。1.2.は対のパターンとして紹介されています。

1.委譲の隠蔽

「1.委譲の隠蔽」は「1.Personクラスで委譲メソッドを提供する」に該当します。

メリットは、クライアント側が委譲先のオブジェクトのことを知らなくてよいことです。

今回の例では、full_nameメソッドを呼び出したい側はNameクラスの存在を意識せずに呼ぶことができます。

リファクタリング』では「(部下が)上司に対して会議に出席できるか尋ねると、手帳をみてから答えます(p.84)」というとても分かりやすい例で説明されています。

sequenceDiagram
    autonumber
    participant Subordinate as 部下
    participant Boss as 上司
    participant Calendar as 手帳(スケジュール)

    Subordinate->>Boss: 出席できますか?(会議日時)
    Boss->>Calendar: 空き確認する(会議日時)
    Calendar-->>Boss: 可否(OK/NG)
    Boss-->>Subordinate: 出席可否を回答(OK/NG)

この例では「上司」は「手帳」への委譲を隠蔽しているといえます。従って、「部下」は上司の手帳を見に行かなくても、上司に聞くだけでほしい回答を得ることができます。

このパターンは「直接の隣人にのみ話しかける」という「デメテルの法則」とも相性がいいです。

ja.wikipedia.org

デメリットはこのパターンを多用すると委譲するだけのメソッドが増えていくことです。例えば、Nameクラスにfull_name_kanaメソッドが追加されたら、Personクラスにもfull_name_kanaメソッドを追加する必要があります。

class Person
  # 省略
  def full_name
    @name.full_name
  end

  def full_name_kana
    @name.full_name_kana
  end
end

このように委譲を過剰に使用した結果、委譲先のオブジェクトに転送するだけのメソッドが多くを占めるようになったクラスをファウラーは「ただの仲介人」と呼んで批判しています。

加えて開発メンバーに「デメテルの法則」が好きな人が多いと、このアンチパターンに陥りやすいと述べられています。

2.仲介人の除去

「1.委譲の隠蔽」は「2.nameフィールド経由で呼ぶ」に該当します。これは委譲を過剰に使用したコードへのリファクタリング手法として紹介されています。

やることは単純で、委譲をやめて委譲先のオブジェクトのメソッドを直接呼ぶことです。

メリット/デメリットは1.の逆で、委譲先のオブジェクトがどのようなメソッドを提供しているかという知識は必要になりますが、「ただの仲介人」を削除することができます。

まとめ

「1.委譲の隠蔽」と「2.仲介人の除去」はそれぞれメリット/デメリットがあるため、混在してもよいとファウラーは主張しています。目安として、よく使われる委譲であれば「1.委譲の隠蔽」を用いてよいとのことです。

加えて、委譲をどの程度隠蔽すべきかはシステムの変化によっても変わるので、都度リファクタリングすることが重要とのことです。




以上の内容はhttps://kamatimaru.hatenablog.com/entry/2026/01/20/001054より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14