背景
Proxyパターンが代理人のクラスを立てて、具象クラスへのアクセスを中継しているのは知っているが、使い所がわからない。
どういう場合に使って、どういう場合に使わないほうが良いのか、具体例で考えてみる。
具体例
Proxyパターンを使うべきケースは、対象へのアクセスにコストがかかる場合や、対象へのアクセスに制限がある場合などである。
つまり、ストライダム大佐を通して範馬勇次郎への戦いを申し込むケースが適切かと考えた。
なぜなら、ストライダム大佐と勇次郎は仲が良いが、中途半端な相手を用意したら勇次郎が激怒する可能性があるので、勇次郎が満足する相手かどうかは代理人であるストライダム大佐が判断し、セッティングする必要があるからである。
ただ、Proxyパターンはクライアント側に、具体的な対象へのアクセスを意識させないことが目的であるため、この例は適切ではないと考え直した。
なぜなら、勇次郎を意識せずに戦いに行く人間はいないからである。
他の例で考えると、HUNTER×HUNTERで、ハンター協会に財宝ハントや、賞金首ハントを依頼したときに、プロハンターに依頼するものは時間がかかるが、ハンターの名前や依頼料金の問い合わせは代理人が実施できる場合を例にしようと考えました。
設計
クライアントは、ハンター協会に対して、「ハント対象設定」メソッドを使って、対象を設定します。
今回の例はゴンとレオリオがいるので、
対象はキメラアントの討伐と、薬の調達が設定できます。
また、「ハンター名取得」はハンター協会が把握しているので、
ゴンやレオリオに問い合わせせずともクライアントに応答することができます。
ポイントとしては、
クライアントが実際のハンターを意識せずハンター協会に依頼することができていますが、
実際には、ハンター協会はハント対象に合わせてゴンやレオリオに依頼しています。
ゴンにキメラアント討伐を依頼する場合は、
「ゴンさん」に変身する必要があるので、
ハンター名を取得するためだけに「ゴンさん」に変身していてはゴンの身が持ちません。(HUNTER×HUNTER キメラアント編 vsピトー を参照ください)
実装
ハンタークラス(インターフェースクラス)
#pragma once
#include <iostream>
#include <string>
using namespace std;
class Ihunter{
public:
virtual string getHunterName()=0;
virtual void setHuntTarget(int target)=0;
virtual void hunt()=0;
};
ハンター協会クラス
#pragma once
#include <iostream>
#include <string>
#include "hunter.h"
#include "gon.h"
#include "reorio.h"
using namespace std;
class hunterAssosiation : public Ihunter{
private:
int _target;
gon* _gon;
reorio* _reorio;
public:
hunterAssosiation(){}
~hunterAssosiation(){}
string getHunterName();
void setHuntTarget(int target);
void hunt();
};
#include <iostream>
#include "hunterAssosiation.h"
using namespace std;
/* Concrete */
string hunterAssosiation::getHunterName(){
string ret;
ret = "hunter:gon, reorio\r\n";
return ret;
};
void hunterAssosiation::setHuntTarget(int target){
_target = target;
};
void hunterAssosiation::hunt(){
if (_target == 0){/* キメラアント討伐はゴンに依頼する */
if(_gon == NULL){
_gon = new gon();
}
_gon->hunt();
}else if (_target == 1){/* レオリオには薬を頼む */
if(_reorio == NULL){
_reorio = new reorio();
}
_reorio->hunt();
}else{
std::cerr << "error\r\n";
}
};
ゴン、レオリオクラス
#pragma once
#include <iostream>
#include <string>
#include "hunter.h"
using namespace std;
class gon : public Ihunter{
public:
gon(){
std::cout << "----seiyaku_to_seiyaku----\r\n";
}
~gon(){}
string getHunterName();
void setHuntTarget(int target);
void hunt();
};
#include <iostream>
#include <string>
#include "gon.h"
/* Concrete */
string gon::getHunterName(){
};
void gon::setHuntTarget(int target){
};
void gon::hunt(){
std::cout << "GONsann:hunt\r\n";
};
#pragma once
#include <iostream>
#include "hunter.h"
using namespace std;
class reorio : public Ihunter{
public:
reorio(){}
~reorio(){}
string getHunterName();
void setHuntTarget(int target);
void hunt();
};
#include <iostream>
#include <string>
#include "reorio.h"
using namespace std;
/* Concrete */
string reorio::getHunterName(){
};
void reorio::setHuntTarget(int target){
};
void reorio::hunt(){
std::cout << "reorio:hunt\r\n";
};
クライアント
#include <iostream>
#include <string>
#include "hunterAssosiation.h"
using namespace std;
int main(){
int target;
string member;
hunterAssosiation* _hunterAssosiation = new hunterAssosiation();
member = _hunterAssosiation->getHunterName();
std::cout << member;
std::cout << "Enter target(0:chimera ant, 1:medical)";
std::cin >> target;
_hunterAssosiation->setHuntTarget(target);
_hunterAssosiation->hunt();
_hunterAssosiation->hunt();
}
実行結果
Proxy % ./test
hunter:gon, reorio
Enter target(0:chimera ant, 1:medical)0
----seiyaku_to_seiyaku----
GONsann:hunt
GONsann:hunt
Proxy % ./test
hunter:gon, reorio
Enter target(0:chimera ant, 1:medical)1
reorio:hunt
reorio:hunt
Proxy %
「----seiyaku_to_seiyaku----」のところで、
ゴンが「ゴンさん」に変身しています。
ゴンとレオリオの名前をクライアントに応答するだけであればわざわざゴンさんになる必要はないので、
「hunter:gon, reorio」のところは、ハンター協会がクライアントに応答しています。
結果、まとめ
今回の嬉しさとしては、
クライアント側に具象クラスを見せずに実現できるvirtual proxyと言われているパターンになります。
ハンター協会に依頼するときは、
何をハントして欲しいかを依頼するだけで、誰に依頼するかまでは意識しなくても良いことになります。
今回の例はゴンとレオリオでしたが、ネテロ会長にしか出来ないことや、ヒソカに依頼しないといけないリスクの高い案件もあるかもしれません。
なのでProxyを用意しておくことは効果的かと思いました。
もしも、ゴンさんになるコストは置いといて、
直接ゴンに依頼したい場合はクライアントがProxyを使わなければ良い話なので、
部品としてゴンクラスは独立して作ることができています。
また、似ているパターンとしてDecoratorパターンの例も作りましたが、
Decoratorパターンは、
オブジェクトに対して新しい機能を追加(複数の機能がどういう順番で呼び出されるか判らないときに使ったりする)するパターン、
Proxyパターンは、
具象クラスの作業を肩代わりして本人へのアクセスを軽減したり、クライアントに具象クラスを意識させないことが目的なので、目的が違います。
ちなみに、レオリオはキャラクターとして、あんま好きじゃないです。笑
キメラアント編は何回も見てしまいますが、ピトーを倒すところは胸がグーっとなります。