参考資料
Bridgeパターン 解釈
目的:
- 実装クラスと機能クラスを分けておくことで、拡張性を向上させること
- 継承によるクラス爆発を防ぐ
僕の身の回りで拡張するケースって何か考えたときに、
グラップラー刃牙の烈海王が最大トーナメントで愚地克巳に言った「キサマ等の居る場所は既に我々が2000年前に通過した場所だッッッ」って言っていたことを思い出し、
空手を中国拳法に拡張しようと考えました。
空手の具象クラスとして愚地克巳クラスがありますが、
空手を継承した中国拳法の具象クラスとしても愚地克巳クラスが必要です。
このように継承したクラスの具象クラスが増えていくので、クラス爆発が問題になるケースで本パターンを使うと効果的です。
クラス
空手の実装クラス「空手_実装」と、機能クラスを分けています。
そうしておくことで、空手の実装部をそのまま使いながら空手という機能を拡張していくことができます。
Bridgeパターンとしては、機能クラスの「空手」にある属性「実装」が、実装クラスと機能クラスの橋渡しをするということでBridgeパターンというらしいです。
パターンを適用しないときのクラス図では愚地克巳クラスはそれぞれ作っていましたが、このパターンだと、継承したとしても具象クラスは増えていきません。
実装(抽象クラス)
- 空手クラス
#pragma once
#include <iostream>
#include "karateImp.h"
class Karate{
protected:
KarateImp* imp;
public:
Karate(KarateImp* karateimplementation) : imp(karateimplementation){}
void punch();
void kick();
};
#include <iostream>
#include "karate.h"
#include "karateImp.h"
/* Concrete */
void Karate::punch(){
imp->punchImp();
}
void Karate::kick(){
imp->kickImp();
}
- 空手_実装 クラス
#pragma once
#include <iostream>
class KarateImp{
public:
virtual void punchImp() = 0;
virtual void kickImp() = 0;
virtual ~KarateImp(){};
};
実装(具象クラス)
#pragma once
#include <iostream>
#include "karateImp.h"
class Orochi : public KarateImp{
public:
void punchImp();
void kickImp();
};
#include <iostream>
#include "karate.h"
#include "karateImp.h"
#include "orochi.h"
/* Concrete */
void Orochi::punchImp(){
std::cout << "Katsumi:mahhaduki\r\n";
}
void Orochi::kickImp(){
std::cout << "Katsumi:keriashi hasami goroshi\r\n";
}
#pragma once
#include <iostream>
#include "karateImp.h"
class Retsu : public KarateImp{
public:
void punchImp();
void kickImp();
};
#include <iostream>
#include "karate.h"
#include "karateImp.h"
#include "retsu.h"
/* Concrete */
void Retsu::punchImp(){
std::cout << "Retsu:guruguru punch\r\n";
}
void Retsu::kickImp(){
std::cout << "Retsu:grobe wo hazusu\r\n";
}
実装(拡張するクラス:中国拳法)
#include <iostream>
#include "karate.h"
class Kungfu : public Karate{
public:
Kungfu(KarateImp* karateimplementation) : Karate(karateimplementation){}
void shaori();
};
#include <iostream>
#include "Kungfu.h"
void Kungfu::shaori(){
std::cout << "shaori\r\n";
}
実装(メイン)
#include <iostream>
#include "karate.h"
#include "karateImp.h"
#include "Kungfu.h"
#include "orochi.h"
#include "retsu.h"
int main(){
KarateImp* karateimp_orochi = new Orochi;
KarateImp* karateimp_retsu = new Retsu;
/* 空手の実装は愚地勝巳 */
Karate* karate = new Karate(karateimp_orochi);
karate->kick();
karate->punch();
/* 空手を進化させた中国拳法を定義、実装は烈海王 */
Kungfu* chinese_kungfu = new Kungfu(karateimp_retsu);
/* 空手を進化させた中国拳法を定義、実装は愚地克巳(愚地克巳の実装部は変えてない) */
Kungfu* japanese_kungfu = new Kungfu(karateimp_orochi);
/* 中国拳法は空手でもできるキック、パンチを継承しているし、追加で消力(シャオリー)が使える */
chinese_kungfu->kick();
chinese_kungfu->punch();
chinese_kungfu->shaori();
/* 愚地克巳が烈海王に教えを乞い、空手を進化させる */
japanese_kungfu->kick();
japanese_kungfu->punch();
japanese_kungfu->shaori();
delete karateimp_orochi;
delete karateimp_retsu;
delete karate;
delete japanese_kungfu;
delete chinese_kungfu;
};
実行結果
Bridge % g++ -o test Karate.cpp main.cpp orochi.cpp Kungfu.cpp retsu.cpp
Bridge % ./test
Katsumi:keriashi hasami goroshi
Katsumi:mahhaduki
Retsu:grobe wo hazusu
Retsu:guruguru punch
shaori
Katsumi:keriashi hasami goroshi
Katsumi:mahhaduki
shaori
ちょっとわかりにくいですが、
最初の
「Katsumi:keriashi hasami goroshi
Katsumi:mahhaduki」は、
空手クラスの実装部に、愚地克巳の具象クラスを割り当てています。
「Retsu:grobe wo hazusu
Retsu:guruguru punch
shaori」
これは、空手クラスを継承した中国拳法クラスの実装クラスに、烈海王の具象クラスを割り当てています。
中国拳法では、空手クラスのパンチ、キックに加えて、消力(シャオリー)を使うことができます。
また、最後の
「Katsumi:keriashi hasami goroshi
Katsumi:mahhaduki
shaori」
の部分は、空手クラスを継承した拳法クラスの実装クラスに、愚地克巳の具象クラスを割り当てています。
なので、蹴り足はさみ殺し、マッハ突きに加えて消力を使うことができるのです。
結論
Bridgeパターンをグラップラー刃牙の例を使って実装してみることで、
使い所をイメージすることができました。
今後の設計のとき、拡張する可能性がある場合などは、実装クラスを分離させておきたいと思います。
ちなみに、
蹴り足はさみ殺し・マッハ突きは最大トーナメントで花山と戦ったときの技、
グローブを外すというのは、最大トーナメントで刃牙と戦ったときに靴を脱いだときのセリフ、ぐるぐるパンチはピクルと戦ったときの技ですね。
よく分からないときは漫画読んでみてくださいね。
刃牙は知ってるけど、デザインパターンのイメージがつかない人に伝わると嬉しいです。