LoginSignup
1
2

More than 5 years have passed since last update.

テンプレートメソッドの解説 -オブジェクト指向設計実践ガイドを読んで-

Last updated at Posted at 2018-09-12

オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方を読んで、テンプレートメソッドについて学習したので解説します。

テンプレートメソッドとは?

スーパークラス内で基本の構造を定義し、サブクラス固有の貢献を得るためにメッセージを送るテクニック

これだけ読んでピンとくる人はいないと思います。

具体的なサンプルを用いた説明

抽象化された言葉で説明されてもこの手の概念はスッキリ理解しづらいところがあります。
とにかく具体例を見て感覚を掴みましょう。

状況設定

今回の例の状況を設定します。

  • 自転車を管理するアプリケーションを作成している
  • アプリケーションでは自転車のスペアパーツを一覧表示できる
  • タイヤのサイズやチェーンのサイズを持っている

クラス同士の関係はこんな感じになっています。

image.png

要件は次のようになっています

  • 自転車はチェーン(chain)とタイヤサイズ(tire size)を持つ
  • 全ての自転車はチェーンについて同じ初期値を共有する
  • サブクラスは、タイヤサイズについて独自の初期値を持つ
  • サブクラスの個々のインスタンスは初期値を無視し、インスタンス固有の値を持つことが許される

bicycle.rb
class Bicycle
  attr_reader :size, :chain, :tire_size

  def initialize(args={})
    @size = args[:size]
    @chain = args[:chain] || default_chain
    @tire_size = args[:tire_size] || default_tire_size
  end

  def default_chain
    '10-speed'
  end
end
road_bike.rb
class RoadBike < Bicycle
  # ...
  def default_tire_size # <- サブクラスの初期値
    '23'
  end
end
mountain_bike.rb
class MountainBike < Bicycle
  # ...
  def default_tire_size # <- サブクラスの初期値
    '2.1'
  end
end

このようにBicycleはサブクラスに共通のアルゴリズムを提供しています。
サブクラスがアルゴリズムに影響を与えることを許す場所に関してBicycleはメッセージ(ここではdefault_tire_size)を送っています。
一方サブクラス側ではそれと同じメソッドを定義することによって、そのアルゴリズムの役割を果たせるようにしています。

このように記述することによって
- 抽象的なベースのクラスには不変的なアルゴリズム
- 具体的なサブクラスには詳細なアルゴリズム

を実現することができます。

不完全なところ

しかし上のコードではまだ不完全な部分があります。
コードが今のままなら不都合は浮き出てきませんが、新たなサブクラスが追加された時に不都合が出てきます。

ここでは例としてサブクラスをRecumbentBikeとします(そういう自転車があるらしい)
上記のアルゴリズムの設計者なら何の問題もなく追加のサブクラスを実装できるかもしれませんが、ここでは設計者以外のプログラマーがサブクラスの追加を行ったとします。

recumbent_bike.rb
class RecumbentBike < Bicycle
  # some code
end

追加を行ったプログラマーが上記の設計を理解しないままdefault_tire_sizeの実装を怠ったとします。
上記の設計はサブクラスにdefault_tire_sizeがあることを前提としているのでもちろんエラーが出ます。

今回のプログラマーが起こすようなエラーを想定して

bicycle.rb
class Bicycle
  attr_reader :size, :chain, :tire_size

  def initialize(args={})
    @size = args[:size]
    @chain = args[:chain] || default_chain
    @tire_size = args[:tire_size] || default_tire_size
  end

  def default_chain
    '10-speed'
  end

+ def default_tire_size
+   raise NoImplementedError,
+     "This #{self.class} cannot respond to:"
+ end
end

このようにエラー文が出るようにしておくことで、もし間違いが起きてもすぐに原因を発見できるようになります。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2