はじめに
この投稿は 増補改訂版 Java言語で学ぶデザインパターン入門 に沿って学んだことを章ごとにまとめるものです.
今回は第3章 Template Method についてまとめます.
Template Method とは
Template Methodってどんなパターン?
- スーパークラス側で抽象メソッドを定義
- スーパークラス内で抽象メソッドの呼び出し方を定義 <- ここが「テンプレート」に相当します
- サブクラスごとに抽象メソッドを定義
何が嬉しいの?
- 大まかな処理の流れを共通化できる
- 処理の中身は違う,流れは共通な処理を呼び出せる
- 大まかの処理の流れの順番等に変更が生じた場合,簡単に修正できる
注意すること
- メソッドの呼び出され方はスーパークラスに実装されているのでスーパークラスの実装を意識する必要がある
- サブクラスが量産されたりするので, Template Method パターンにこだわりすぎないように注意
実装例
目的
今回は顔文字を出力するためのクラスを作成します.
今回はスーパークラスとして Protocol
を使用しました.
実装
Emoticon プロトコル
顔文字を出力するために必要なメソッドやプロパティを定義したプロトコル
protocol Emoticon {
/// 顔の文字列 (例: "^o^")
var face: String { get }
/// 顔の文字列で初期化
init(face: String)
/// 顔の左側の文字列 (例: "\(")
func makeLeftSide() -> String
/// 顔の右側の文字列 (例: ")/")
func makeRightSide() -> String
/// 顔文字の表示 (上記の例では "\(^o^)/" が表示される)
func printEmoticon()
}
extension Emoticon {
/// 顔文字の表示メソッドの実装
func printEmoticon() {
// プロトコルで定義されたまだ中身のないメソッドを使用して顔文字を作る
// ここがテンプレート部分にあたる
let emoticon = makeLeftSide() + face + makeRightSide()
print(emoticon)
}
}
Normal クラス
基本的な ()
で囲まれた顔文字用のクラスです.
final class Normal: Emoticon {
let face: String
required init(face: String) {
self.face = face
}
func makeLeftSide() -> String {
return "("
}
func makeRightSide() -> String {
return ")"
}
}
Crab クラス
v()v
で顔を囲まれた顔文字用のクラスです.
かにクラスってなんかテンション上がりません?(?)v(`・ω・´)vかに
実装は Normal クラスと同じです.
final class Crab: Emoticon {
let face: String
required init(face: String) {
self.face = face
}
func makeLeftSide() -> String {
return "v("
}
func makeRightSide() -> String {
return ")v"
}
}
動作確認
実装は以上です.
実際に Normal クラスと,かにクラスにシャキーンの顔を渡してみます.
Normal(face: "`・ω・´").printEmoticon() // (`・ω・´)
Crab(face: "`・ω・´").printEmoticon() // v(`・ω・´)v
このように,同じ printEmoticon()
メソッドを呼んでおり,スーパークラス(今回はプロトコル)の中身も同じですが, makeLeftSide()
と makeRightSide()
の実装が異なるため,違う内容が出力されました.
まとめ
今回は Template Method パターンをまとめました.
Template Method の形
AbstractClass (抽象クラス) 役
今回は Emoticon
プロトコルがこの役をしました.
サブクラスで定義する抽象メソッドの呼び出し方や大まかな流れをここで定義しました.
ConcreteClass (具象クラス) 役
今回は Normal
クラスと,かにクラスにあたります.
それぞれの Emoticon
プロトコルに呼び出されるメソッドを実装しました.
クラス図
思ったこと
- 基本の基本,という感じの考え方だと思います,自然に使っていきたい.
- 継承の形を使用するので,
templateMethod
内でのみ使用したいメソッドを抽象メソッドとして定義するべきではなさそう. (そういう意味では今回の実装例はあまりよろしくない)- 単体でも使う想定のメソッドを抽象メソッドとして定義するべき (クラスの外から呼び出すことができてしまうため)