Facadeパターン

デザインパターン【C#】:Facade 04_フォルダー構成を工夫する

前回までのレクチャーで、ファサードクラス(MachineFacadeクラス)の作成が完了しました。クライアント側のコードから確認してみると、「MachineFacade.」と入力した際のインテリセンスには、BacklightOffやFanSpin、PowerOffといった、ファサードが公開しているメソッドだけが一覧表示されるようになっています。クライアントから見ると非常にシンプルで使いやすい状態になっていることがわかります(図1)。

図1 MachineFacade.と入力した際のインテリセンス。ファサードが公開するメソッドのみが表示される

実際にこのとき画面に表示されているファサードクラスの中身を確認すると、次のように、サブシステム内の各クラス(Fan、Power など)への呼び出しをファサードの中に隠蔽し、クライアントにはシンプルなメソッド群だけを公開する、という作りになっています。

public static class MachineFacade
{
    // BacklightOff / BoxExternalTemperature / BoxInternalTemperature /
    // CameraTake なども同様の要領で定義されている(インテリセンス一覧より)
 
    public static FanEntity FanSpin(int fanId)
    {
        return new Fan().GetSpin(fanId);
    }
 
    public static void FanStart(int fanId)
    {
        new Fan().Start(fanId);
    }
 
    public static void FanStop(int fanId)
    {
        new Fan().Stop(fanId);
    }
 
    public static void PowerOn()
    {
        new Power().On();
    }
 
    public static void PowerOff()
    {
        new Power().Off();
    }
}

 

 

FanSpinやFanStart、PowerOnといったメソッドの内部では、サブシステムのFanクラスやPowerクラスを直接newして呼び出しています。クライアント側はこれらのサブシステムのクラスを意識することなく、ファサード経由でまとめて操作できるというわけです。

ファサード以外のクラスも見えてしまっている

ただし、ファサードクラスに用意されているメソッドだけがクライアントから見えているわけではありません。試しに「Machine.」まで入力してインテリセンスを見てみると、Box、Camera、Fan、FanEntity、MachineFacade、Power、UIというように、ファサード以外のクラスもクライアント側から普通に見えてしまっていることがわかります(図2)。

図2 「Machine.」まで入力した際のインテリセンス。ファサード以外のクラスもすべて表示されてしまっている

本来、クライアントが直接呼び出す必要のないクラスやメソッドは、できるだけ見えないようにしておいたほうが、クライアント側の実装はしやすくなります。使わなくてよいクラスまでパブリックに公開されていると、それだけ選択肢が増えてしまい、「結局どれを使えばいいのか」と迷う原因になってしまうためです。クラスやメソッドは、極力必要のないものは公開しないようにするのが望ましいといえます。

次回以降のレクチャーでは、こうした不要なクラスにアクセス修飾子を設定し、クライアントから見えないようにしていきます。ただし、その作業に入る前に、本レクチャーではまずフォルダー構成を整理しておきたいと思います。

現状のフォルダー構成の問題点

現時点のソリューションエクスプローラーを確認すると、Box.cs、Camera.cs、Fan.cs、FanEntity.cs、MachineFacade.cs、Power.csが、すべて同じ階層(プロジェクト直下)にザラザラと並んでいます(図3)。

図3 整理前のMachineプロジェクト。ファサードを含むすべてのクラスが同じ階層に並んでいる

この状態だと、ファサードクラスであるMachineFacade.csが他の多くのクラスに埋もれてしまい、「このサブシステムにはファサードが用意されている」ということが、ソリューションエクスプローラーをひと目見ただけでは伝わってきません。フォルダー構成についても、もう少し工夫したほうがよさそうです。

Objectsフォルダーを作成する

そこで、ファサード以外のクラスをまとめて格納するための専用フォルダーを作成します。Machineプロジェクトを右クリックし、「追加」を選択します(図4)。

図4 Machineプロジェクトを右クリックして表示されるコンテキストメニュー。「追加」からフォルダーを作成する

「追加」から新しいフォルダーを作成すると、フォルダー名を入力できる状態になります。ここでは「Objects」という名前を付けています(図5)。なお、このフォルダー名は必ずしも「Objects」である必要はなく、実際に格納するオブジェクトの性質に合わせて、自由な名前に変更してもらって構いません。

図5 作成したフォルダーの名前を入力しているところ。ここでは「Objects」と名付けている

ファサード以外のクラスをフォルダーへ移動する

Objectsフォルダーを作成したら、Box.cs、Camera.cs、Fan.cs、FanEntity.cs、Power.csをドラッグ&ドロップでObjectsフォルダーの中へ移動します。MachineFacade.csだけは、そのままプロジェクト直下に残しておきます。移動が完了した直後のプロジェクト直下は、Objectsフォルダーと MachineFacade.cs の2つだけになります(図6)。

図6 サブシステムのクラスをすべてObjectsフォルダーへ移動した直後の状態

Objectsフォルダーを展開してみると、その中にBox.cs、Camera.cs、Fan.cs、FanEntity.cs、Power.csが収まっており、MachineFacade.csだけがフォルダーの外(プロジェクト直下)に置かれている状態になっていることが確認できます(図7)。

図7 Objectsフォルダーを展開した状態。サブシステムのクラス群がフォルダー内にまとまっている

フォルダーを閉じた状態で確認する

最後に、Objectsフォルダーを閉じた状態で確認してみます(図8)。プロジェクト直下にはObjectsフォルダーとMachineFacade.csの2つだけが並ぶ、非常にすっきりとした見た目になりました。

図8 Objectsフォルダーを閉じた状態。プロジェクト直下にはファサードだけが直置きされている

この状態であれば、ソリューションエクスプローラーをパッと見ただけで、「このサブシステムにはファサードが用意されている」「呼び出すときはこのファサード経由で呼べばよい」ということが一目で伝わるようになります。

なお、Objectsフォルダーの中身をさらに用途ごとに2個・3個、あるいはそれ以上のフォルダーに分割しても、まったく問題ありません。重要なのは、フォルダーに入れずプロジェクト直下に直置きするクラスは、ファサードだけに絞っておくという点です。直置きされているものがファサードだけであれば、それだけファサードの存在が目立ち、サブシステムの入り口がどこかがひと目で伝わるようになります。

まとめ

本レクチャーでは、実際にアクセス修飾子を変更してクラスを非公開にしていく作業に入る前段階として、フォルダー構成を整理しました。ファサード以外のクラスをObjectsフォルダーへ集約し、ファサードクラスだけをプロジェクト直下に残すことで、「このサブシステムの入り口はどこか」が一目でわかるようになります。次回以降のレクチャーでは、いよいよ各クラスのアクセス修飾子を変更し、クライアントから見えるクラス・メソッドを必要最小限に絞り込んでいきます。

C#文法:コレクション

■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る

A01_はじめに
A02_プロジェクトの作成
B01_配列とは
B02_配列の生成とアクセス
B03_生成と同時に値を設定する
B04_型推論による生成
B05_メソッドの引数などにする場合の注意点
B06_Length
B07_IndexOfでの検索
B08_FindIndexでの検索
B09_Find
B10_Exists
B11_FindAllとFindLast
B12_誤ったコピー
B13_Array.Copy
B14_範囲指定のコピー
B15_Resize
C01A_ArrayList
C02_List
C03_List 動的な要素の変更
C04_Listのコンストラクタ
C05_Listのコンストラクタ_Capacity
C06_ListTからArrayクラスのメソッドが呼ばれている
D01_コレクションインタフェースとは
D02_異なるコレクションクラスに互換性を持たせる
D03_インタフェースの階層構造
D04_クライアントができる操作を制限させる
D05_クライアントができる操作を制限させる_後半
D06_Enumerableの拡張メソッドに関して
D07_ReadOnly系のコレクションインタフェース
D08_AsReadOnly
D09_ToListでコピーする
D10_ListTはprivateで使う

■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る