デザインパターンってなに?
プログラミングをしていると以前と同じことを書いていると気づくことがあると思います.そのような普遍的なノウハウを蓄積し、名前をつけて再利用しやすいようにまとめることで効率的に開発を進めることができます.
4人の偉大なプログラマ Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides) はそれらのノウハウをまとめました.それが「デザインパターン」です.
デザインパターンを学べばより効率的に,安全に開発を進めることができます!
この記事ではJava言語で学ぶデザインパターン入門を参考にデザインパターンの1つTemplate Method パターンについて「鬼滅の刃」を例に説明していきます.
この記事の対象
- デザインパターンに興味があるが,よく知らない人
- 鬼滅の刃を読んでいる(アニメを見ている)
Template Method ってどんなデザインパターン?
Template Methodとはテンプレートの機能をもつデザインパターンです.テンプレートとは処理の枠組みのことです.スーパークラスでテンプレートを定義して大枠の処理を定めます.そして具体的な処理はサブクラスに任せます.実際のコードをみた方が分かりやすいと思うので早速実装していきましょう!
呼吸の型をテンプレートとして考える
鬼殺隊員は基本的に10個の呼吸の型を持っています(できるかどうかは置いておいて).そして技を繰り出す時は全集中の呼吸を行います.ここまでは鬼殺隊員が持つ共通性質です.ここがテンプレート部分にあたります.しかし,同じ壱ノ型でも炭治郎が使う壱の型と伊之助が使う壱の型は異なります.具体的な呼吸の型はキャラクターによって異なるわけです.そこで呼吸の型の定義と呼吸の発動アルゴリズムをスーパークラスで行い,具体的な呼吸の型のアルゴリズムはサブクラスに任せて実装します.ソースコードが長くなってしまうので今回は呼吸の型は参ノ型まで実装します.
鬼殺隊員クラス (スーパークラス)
鬼殺隊員クラスはfirstForm, secondForm, thirdForm, breathigというメソッドを持つ抽象クラスです.このうち呼吸の型を表現するfirstForm, secondForm, thirdForm は抽象メソッドで,breathingメソッドのみが実装されています.breathingメソッドは全集中の呼吸を行うメソッドで,formNumberで指定された番号の型を繰り出します.
firstForm, secondForm, thirdForm が実際に何をするかは鬼殺隊員クラスを見ただけでは分かりません.繰り出す技の内容はサブクラスである各キャラクタークラスにまかせています.
public abstract class DemonSlayer {
public abstract void firstForm(); // 壱ノ型 中身の実装はサブクラスに任せる
public abstract void secondForm(); // 弍ノ型 中身の実装はサブクラスに任せる
public abstract void thirdForm(); // 参ノ型 中身の実装はサブクラスに任せる
public final void breathing(int formNumber) { // 全集中の呼吸で技を繰り出す
if (formNumber == 1) {
firstForm();
}
if (formNumber == 2) {
secondForm();
}
if (formNumber == 3) {
thirdForm();
}
System.out.println("");
}
}
炭治郎クラス (サブクラス)
スーパークラスである鬼殺隊員クラスを実装したのでそのサブクラスのキャラクターのクラスを作っていきましょう.まず主人公の炭治郎を作っていきます.炭治郎クラスは鬼殺隊員クラスを継承します.ここでスーパークラスでメソッドの定義のみされていた呼吸の型のメソッドの中身を実装していきます.炭治郎には水の呼吸の型を実装していきます.
public class Tanjiro extends DemonSlayer { // 鬼殺隊員クラスを継承
public Tanjiro() {
}
public void firstForm() { // 壱ノ型
System.out.println("水の呼吸、壱ノ型!");
System.out.println("〜〜〜〜〜〜");
System.out.println("=" + "水面切り" + ">");
System.out.println("〜〜〜〜〜〜");
}
public void secondForm() { // 弍ノ型
System.out.println("水の呼吸、弍ノ型!");
System.out.println("卍ーー卍");
System.out.println("|" + "水車" + "|");
System.out.println("卍ーー卍");
}
public void thirdForm() { // 参ノ型
System.out.println("水の呼吸、参ノ型!");
System.out.println("\〜〜〜〜/");
System.out.println("ー" + "流流舞い" + "ー");
System.out.println("/〜〜〜〜\");
}
}
ここでbreathigメソッドが呼ばれたらどうなるでしょうか?
たとえば引数が1でbreathigメソッドが呼ばれた場合,以下のように表示されます
public class Main {
public static void main(String[] args) {
DemonSlayer tanjiro = new Tanjiro();
tanjiro.breathing(1);
}
}
水の呼吸、壱ノ型!
〜〜〜〜〜〜
=水面切り>
〜〜〜〜〜〜
華麗な水面切りを放つことができました!
ここでは,スーパークラスのbreathigメソッドからfirstFormメソッドが呼び出されています.
これと同様に伊之助,善逸クラスも定義していきます.
伊之助クラス (サブクラス)
public class Inosuke extends DemonSlayer { // 鬼殺隊員クラスを継承
public Inosuke() {
}
public void firstForm() { // 壱ノ型
System.out.println("獣の呼吸、壱ノ牙!");
System.out.println("|> <|");
System.out.println("|" + "穿ち抜き" + "|");
System.out.println("| |");
}
public void secondForm() { // 弍ノ型
System.out.println("獣の呼吸、弍ノ牙!");
System.out.println(" |> ");
System.out.println("ー" + "切り裂き" + "ー");
System.out.println(" |> ");
}
public void thirdForm() { // 参ノ型
System.out.println("獣の呼吸、参ノ型!");
System.out.println("\ /");
System.out.println("ー" + "喰い裂き" + "ー");
System.out.println("/ \");
}
}
善逸クラス (サブクラス)
public class Zenitsu extends DemonSlayer { // 鬼殺隊員クラスを継承
public Zenitsu() {
}
public void firstForm() { // 壱ノ型
System.out.println("雷の呼吸、壱ノ型!");
System.out.println("⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡");
System.out.println("=====" + "霹靂一閃" + "=====>");
System.out.println("⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡");
}
public void secondForm() { // 弍ノ型
}
public void thirdForm() { // 参ノ型
}
}
いざ,鬼退治へ!!
それではそれぞれのキャラクターのインスタンスを作成していざ,鬼退治へ出かけましょう.
public class Main {
public static void main(String[] args) {
DemonSlayer tanjiro = new Tanjiro();
DemonSlayer zenitsu = new Zenitsu();
DemonSlayer inosuke = new Inosuke();
tanjiro.breathing(1);
tanjiro.breathing(2);
tanjiro.breathing(3);
inosuke.breathing(1);
inosuke.breathing(2);
inosuke.breathing(3);
zenitsu.breathing(1);
zenitsu.breathing(2);
zenitsu.breathing(3);
}
}
水の呼吸、壱ノ型!
〜〜〜〜〜〜
=水面切り>
〜〜〜〜〜〜
水の呼吸、弍ノ型!
卍ーー卍
|水車|
卍ーー卍
水の呼吸、参ノ型!
\〜〜〜〜/
ー流流舞いー
/〜〜〜〜\
獣の呼吸、壱ノ牙!
|> <|
|穿ち抜き|
| |
獣の呼吸、弍ノ牙!
|>
ー切り裂きー
|>
獣の呼吸、参ノ型!
\ /
ー喰い裂きー
/ \
雷の呼吸、壱ノ型!
⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡
=====霹靂一閃=====>
⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡
無惨「🥺」
Template Method についてもう少し詳しく
無事,鬼退治ができたところでTemplate Methodについて少し考えていきます.
ロジックの共通化
サブクラス共通のアルゴリズムをスーパークラスに記述することでサブクラス側ではいちいちアルゴリズムを記述しなくてよくなります.これはサブクラスが多い場合に大きなメリットとなります.
Tempalte Methodを用いない場合を考えてみます.まず炭治郎クラスを作成し,それをコピーして伊之助,善逸,富岡義勇,胡蝶しのぶ... etc を実装していったとします.ここでもし炭治郎クラスでバグが見つかった場合,すべての鬼殺隊員のクラスを修正することになってしまいます.
抽象クラスの意義
今回,鬼殺隊員クラスは抽象クラスで実装しました.抽象クラスはインスタンスを作ることができません.抽象クラスを初めて学習するとき抽象クラスの存在意義がいまいち分からないなんてことがあると思います(私もそうでした!).しかし,Template Methodを理解すると抽象クラスの存在意義も分かってきます.実際の呼吸の型の実装はしなくとも,型の定義とおおまかな型の発動の流れを抽象クラスで実装するのは大切なことです.