Façadeパターン


 ★☆☆

Façadeパターン

「窓口」となるクラスを作ってシンプルに利用する

façadeとはフランス語を語源とする単語で「建物の正面」という意味です。発音するときはファサードの「サ」にアクセントを置きます。

プログラムを作っていくと、最初は小さなものでも、だんだん大きくなっていきます。 たくさんのクラスが出来て、相互に関係しあい、複雑になっていきます。 クラスを使う場合には、それらの関係を正しく理解して、 正しい順番にメソッドを呼び出す必要があります。 大きなプログラムを使って処理を行う場合、 関係しあっているたくさんのクラスを適切に制御しなくてはいけません。 その処理を行うための「窓口」を用意しておくと、 個別にたくさんのクラスを制御しなくても、「窓口」に対して、要求するだけですみます。


例えば、市役所などに申請書類を提出しなければならないとき、いろいろな課をまわって許可をもらうなどしていては、時間もかかりますし、初めてではまずどこに行ったらいいかも分からないかもしれません。しかし、総合窓口のようなところに申請書類を提出するだけですめば簡単です。誰でも簡単に申請できるわけです。

Façadeパターンは、既存のクラスを複数組み合わせて使う手順を、「窓口」となるクラスを作ってシンプルに利用できるようにするパターンです。


つまり、 Façadeパターンとは、インタフェースを少なくすることです。クラスやメソッドがたくさん見えてしまうと、どれを使っていいか迷いますし、また、呼び出しの順番にも注意しなければなりません。注意しなければならないということは、間違いやすいということです。そして、インタフェースが少ないということは、部品としての再利用もしやすなるということです。

例えば、データベースに対し何でもできてしまうクラスがあったとします。今、データベースを検索するだけのプログラムを作ろうとしているときに、そのような何でもできてしまうクラスは必要ありません。削除や更新のメソッドはいらないわけです。これらのメソッドがあると、誤って使ってしまうこともあるでしょうし、どのメソッドを使うかの選択肢が多くなって迷ってしまうこともあるかもしれません。このようなときには、その何でもできてしまうクラスを継承して、あるいはそのクラスに処理を委譲することによって、メソッドを検索関係だけに絞ることも必要なのです。

また、クラスを設計するときには、どのメソッドを public にするかをよく考える必要があります。あまりに多くのメソッドを public にしてしまうと、クラス内部を修正しにくくなります。


Façadeパターンは、パターンといえばパターンですが、ある程度の規模になれば、だれでも使うものです。

しかし、複雑なプログラムの内部を熟知しているプログラマーは、 Façade を作りたがらないかもしれません。それは、その熟練プログラマーの頭の中には、システムの内容がすべて入っていて、たくさんのクラスの相互関係が分かっているからかもしれません。あるいは、自分の技術を誇り、他のプログラマーに優越感をもてるからかもしれません。

あるプログラマーが「このクラスを呼ぶ前にはこっちを呼ばなくちゃいけない。こっちのメソッドの呼び出し前には、このクラスに登録しておく必要がある。」といった話を「得意げに」するときには、 Façadeパターンを導入する必要があることを示唆しています。

はっきりと言葉で表現できるノウハウは、プログラマーの頭の中に隠しておくべきものではなく、コードとして表現しておくべきものなのです。


例題

文字列の暗号化、復号化を行います。


実行結果
7eYGP5X8qpgURMdp+7VKQvF39KUQ4Q+BIKM+g6vOj70=        暗号化の方式で異なる
暗号化する文字列  







Façadeパターンを使用しない例

Main.java
public class Main { public static void main(String[] args) { // 対象文字列 String plainText = "暗号化する文字列"; // 暗号化キー String encryptingKey = "ABCDEFGHIJKLMNOP"; // 暗号化する SecretKeySpec secretKey = new SecretKeySpec(encryptingKey.getBytes(), "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] encryptedData = cipher.doFinal(plainText.getBytes()); String encryptedText = new String(Base64.getEncoder().encode(encryptedData)); //暗号化文字列の表示 System.out.println(encryptedText); // : // : // 復号化する cipher.init(Cipher.DECRYPT_MODE, secretKey); encryptedData = Base64.getDecoder().decode(encryptedText); byte[] decryptedData = cipher.doFinal(encryptedData); String decryptedText = new String(decryptedData); // 結果表示 System.out.println(decryptedText); } }

Façadeパターンを使用した例

AES.java
public class AES { public static String encrypt(String plainText, String encryptingKey) throws Exception { SecretKeySpec secretKey = new SecretKeySpec(encryptingKey.getBytes(), "AES"); Cipher cipher = Cipher.getInstance("AES"); // 暗号化する cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] encryptedData = cipher.doFinal(plainText.getBytes()); return new String(Base64.getEncoder().encode(encryptedData)); } public static String decrypt(String encryptedText, String encryptingKey) throws Exception { byte[] encryptedData = Base64.getDecoder().decode(encryptedText); SecretKeySpec secretKey = new SecretKeySpec(encryptingKey.getBytes(), "AES"); Cipher cipher = Cipher.getInstance("AES"); //復号化する cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] decryptedData = cipher.doFinal(encryptedData); return new String(decryptedData); } }
Main.java
public class Main { public static void main(String[] args) { // 対象文字列 String plainText = "暗号化する文字列"; // 暗号化キー String encryptingKey = "ABCDEFGHIJKLMNOP"; //暗号化する String encryptedText = AES.encrypt(plainText, encryptingKey); //暗号化文字列の表示 System.out.println(encryptedText); // : // : //復号化する String decryptedText = AES.decrypt(encryptedText, encryptingKey); // 復号化文字列の表示 System.out.println(decryptedText); } }

この例では、比較的単純でクラスも2つ(SecretKeySpec、Cipher)しか利用していません。それでも、どのようにインスタンスを生成して、メソッドは何を呼んで、引数は何で...と、手順を踏まなければなりません。もしこれがいつも決まりきった手順なら、あらかじめ用意しておけばよいのです。

Façadeパターンでは、複雑な手順は、「窓口」となるクラスを作ってその中に入れたため、シンプルに利用できるようになりました。