プロキシパターン
プロキシパターン(Proxy Pattern)では、あるクラスが別のクラスの機能を代表します。このタイプのデザインパターンは構造型パターンに分類されます。
プロキシパターンは、プロキシオブジェクトを導入することで元のオブジェクトへのアクセスを制御します。プロキシオブジェクトはクライアントとターゲットオブジェクトの間で仲介役となり、クライアントのリクエストをターゲットオブジェクトに転送し、リクエストの転送前後に追加の処理を行うことができます。
プロキシパターンでは、既存のオブジェクトを持つオブジェクトを作成し、外部に機能インターフェースを提供します。
はじめに
意図
他のオブジェクトにプロキシを提供し、そのオブジェクトへのアクセスを制御します。
主な解決課題
- プロキシパターンは、オブジェクトを直接アクセスする際に発生し得る問題、例えばオブジェクト生成コストが高い、セキュリティ制御やリモートアクセスが必要などの問題を解決します。
使用シーン
- オブジェクトにアクセスする際に何らかの制御や追加処理が必要な場合。
実装方法
- 中間層を追加する:プロキシクラスを作成し、実際のオブジェクトの中間層とします。
- プロキシと実際のオブジェクトの組み合わせ:プロキシクラスは実際のオブジェクトへの参照を保持し、アクセス時に制御を行います。
キーコード
- プロキシクラス:実際のオブジェクトと同じインターフェースを実装し、追加の制御ロジックを加えます。
- 実際のオブジェクト:実際にタスクを実行するオブジェクト。
応用例
- ショートカット:Windowsシステムのショートカットは、ファイルやプログラムのプロキシとして機能します。
- なりすまし:孫悟空が高翠蘭のプロキシとなり、猪八戒は見分けられません。
- 代理販売店:電車の切符を購入する際、代理販売店が駅のプロキシとして機能します。
- 小切手:銀行口座の資金のプロキシとして、資金へのアクセスを制御します。
- Spring AOP:プロキシパターンを使用してアスペクト指向プログラミングを実現します。
利点
- 責任の分離:プロキシパターンはアクセス制御とビジネスロジックを分離します。
- 拡張性:追加の機能や制御を柔軟に追加できます。
- インテリジェント化:遅延ロードやキャッシュなど、アクセスリクエストをインテリジェントに処理できます。
欠点
- パフォーマンスオーバーヘッド:プロキシ層が追加されることで、リクエストの処理速度に影響を与える可能性があります。
- 実装の複雑さ:一部のタイプのプロキシパターンは実装が複雑になる場合があります。
使用上のアドバイス
- 具体的なニーズに応じて、リモートプロキシ、仮想プロキシ、保護プロキシなどの適切なプロキシタイプを選択してください。
- クライアントが透過的にプロキシを使用できるように、プロキシクラスと実際のオブジェクトのインターフェースを一致させてください。
注意事項
- アダプターパターンとの違い:アダプターパターンはインターフェースを変更しますが、プロキシパターンはインターフェースを変更しません。
- デコレーターパターンとの違い:デコレーターパターンは機能の拡張に使用され、プロキシパターンはアクセス制御に使用されます。
構造
主に以下のコアロールが関与します:
抽象テーマ(Subject):
- 実際のテーマとプロキシテーマの共通インターフェースを定義します。これにより、実際のテーマを使用するあらゆる場所でプロキシテーマを使用できます。
実際のテーマ(Real Subject):
- 抽象テーマインターフェースを実装し、プロキシオブジェクトが表す実際のオブジェクトです。クライアントは実際のテーマに直接アクセスしますが、特定の状況ではプロキシテーマを介して間接的にアクセスできます。
プロキシ(Proxy):
- 抽象テーマインターフェースを実装し、実際のテーマへの参照を保持します。プロキシテーマは通常、実際のテーマに基づいて、遅延ロード、権限制御、ログ記録などの追加機能を提供します。
クライアント(Client):
- 抽象テーマインターフェースを使用して実際のテーマまたはプロキシテーマを操作し、具体の実装クラスがどれであるかを知る必要はありません。
実装
以下を作成します:Imageインターフェースと、それを実装したImageインターフェースのエンティティクラス。ProxyImageプロキシクラスであり、削減しますRealImageオブジェクトの読み込みによるメモリ消費。
ProxyPatternDemoクラスは使用しますProxyImageを使用して、読み込むImageオブジェクトを取得し、必要に応じて表示します。
ステップ 1
インターフェースを作成します。
Image.java
public interface Image {
void display();
}
ステップ 2
インターフェースを実装するエンティティクラスを作成します。
RealImage.java
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName){
System.out.println("Loading " + fileName);
}
}
ProxyImage.java
public class ProxyImage implements Image{
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName){
this.fileName = fileName;
}
@Override
public void display() {
if(realImage == null){
realImage = new RealImage(fileName);
}
realImage.display();
}
}
ステップ 3
リクエストされたとき、使用しますProxyImageを使用して取得しますRealImageクラスのオブジェクト。
ProxyPatternDemo.java
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");
//画像はディスクから読み込まれます
image.display();
System.out.println("");
//画像はディスクから読み込む必要はありません
image.display();
}
}
ステップ 4
プログラムを実行し、結果を出力します:
Loading test_10mb.jpg Displaying test_10mb.jpg Displaying test_10mb.jpgその他の拡張