class なんかすごいマネージャー
{
クラス A を生成
クラス B を生成
クラス C を…
…以下、延々と続く
クラス A を生成する前の準備
クラス B を生成する前の準備
クラス C を…
…以下、延々と続く
クラス B の状態を変える
クラス D の状態を変える
…以下、延々と続く
クラス B の状態を返す
クラス C の…
…以下、延々と続く
クラス A のコールバック先
クラス B の…
…以下、延々と続く
}
省略しているが、扱うクラスは10個以上。
メソッドの実装コードを含めると1000行を超える
たまにものすごく記憶力が良い人がいるのですが、ごく一部です。
20個程度のコーディングルールすら覚えられない(必要なときに思い出せない)のが普通です。
「なんかすごいマネージャークラス」が何をしてくれるクラスなのか?を把握するには、ある程度、メソッドを覚えておく必要があります。
メソッドの数が増えるほど、難しくなります。
クラス A のメソッド、B の、C …と順番通りに並んでいればまだいい方で、定義する順序はバラバラになりがち。
「こういうメソッドあったよなー名前なんだっけ?」と思ってコードをざっと探しても、1000行以上ある中から、たったの1行を探し出すのは時間がかかります。
最近のコードエディタは入力候補を提示してくれますが、入力候補の数が膨大になると選ぶのにも時間がかかります。
時間がかかる = 開発効率が落ちる
1000行を超えるコードを読むのが大好きな人は非常に少ないです。
この巨大なクラスに何かを追加したくない…修正したくない…という理由から、歪なコードが量産されるかも知れません。
歪なコードは保守を困難なものにします。
ソフトウェア開発は八割が保守です。
プロジェクトが大きくなると、実装する人と、保守する人が同じことはほとんどありません。
自分が保守担当になると、他人のコードをある程度理解しないといけません。
コード量が増えるほど、理解するまでの時間が長くなります。
時間がかかる = 開発効率が落ちる
1.クラス A B C …へのアクセス方法を1つに絞りたかった。
・クラス A B C …のインスタンスを1つのクラスで管理できるので楽。
・クラス A B C …にアクセスする方法をその都度探す必要がないので楽。
2.クラス A B C …が持っている一部のメソッドにだけアクセスできるようにしたかった。
ホーム画面に表示する全ての UI にアクセスするための HomeUIManager とか、戦闘中に表示する全ての HUD にアクセスするための BattleHUDManager とか、ワールドに出現している敵の状態を調べたり操作したりする EnemyManager とか、ゲームでは無数のマネージャークラスを作りますが、いずれも共通点を持った情報を一か所に集約して参照しやすくするためのものです。
限られたクラス(クラス A だけ…など)のインスタンスを大量に生成して、それを管理するマネージャーなら、このような状況にはならないと思いますが、今回のように管理するクラスが多い場合は、それに合った設計が必要でした。
そうでない場合もあると思いますが、今回のケースはこの1つだけでした。
やることが明確になれば改善方法も見えてきます。
class なんかすごいマネージャー
{
クラス A のアクセサ
クラス B のアクセサ
クラス C の…
以下、延々と続く
}
class クラス A のアクセサ
{
クラス A を生成する前の準備メソッド
クラス A を生成するメソッド
クラス A のコールバック先登録メソッド
}
class クラス B のアクセサ
クラス C の
…
クラスへのアクセスを制限する必要がない場合はアクセサは必要ないので、インスタンスへの参照を返せば良いです。
アクセサと呼ばれる、「クラスにアクセスするためのクラス」を間に挟んで、「なんかすごいマネージャー」はアクセサだけ持つ形にします。
クラス B の「ほげほげメソッド」の正確なメソッド名が知りたいような状況では、とりあえずクラス B が持っていることは分かっているので、クラス B のアクセサを調べるだけで済みます。
なんかすごいマネージャーが20個のクラスを管理していても、20個+α 調べるだけで良いです。
1000行以上ある中から、たった1行を探し出すよりずっと効率が良いです。
大雑把で良いなら、以下のパターンに当てはまります。
「なんかすごいマネージャー」自体は、ファサード(Facade)(正面玄関)パターンです。
ファサードパターンは、クラス A B C…などのサブシステムへのアクセス方法を制限するためのものです。
クラス A B C…の全てのメソッドやプロパティーにアクセスできるわけではなく、必要最低限のアクセスを提供します。
クラス A B C …のアクセサが、いろんな処理を挟んでから、クラス A B C …にアクセスするなら、メディエイター(Mediator)(仲介人)パターンです。
アクセサがクラス A B C …との間に入って仲介する構造になるので。
マネージャーと名前がつくクラスは、シングルトン(Singleton)パターンを使うことが多いです。
「なんかすごいマネージャー」のインスタンスが複数ある場合は、どのインスタンスを使えば良いのか迷いますが、1つしかないなら、それだけ使えば良いので楽です。
シングルトンにするのは、インスタンスを複数作られると困るとか、インスタンスを複数作らないことが明確に決まっている場合だけです。
シングルトンにするために多少手間がかかるので、なんでもかんでもシングルトンにすると実装でも保守でもコストが増えて開発効率が落ちます。
クラス A のメソッドにはないけど、クラス A のプロパティやインスタンスを参照して何らかの処理を行うメソッドを、「なんかすごいマネージャー」に追加したい状況もあると思います。
それもクラス A のアクセサに追加すれば良いです。
クラス A だけでなく、A1, A2, A3 みたいな派生クラスがあって、状況によって、A1, A2, A3 のどれにアクセスするかを変えないといけない…みたいなときに、クラス A のアクセサを作っておくと、アクセサの中で切り替えれば良いので対応が楽になりそうです。
クラス A B C …のうち、複数のクラスに対してまとめて処理を行うようなメソッドは、「なんかすごいマネージャー」が持った方が良いです。
マネージャー(管理者)なので、管理しているメンバー(クラス A B C …)全てに対して何かをしたい状況はままあると思います。