メインコンテンツへ移動

デザインパターンにおけるプロキシパターン(Proxy Pattern)_補足編

無料2015-03-07#Design_Pattern#代理模式#Proxy Pattern

前の記事ではプロキシパターンについて詳しく説明し、リモートプロキシを詳細に紹介しました。この記事では、他のプロキシ(仮想プロキシ、保護プロキシ、ファイアウォールプロキシなど)を補足します。

no_mkd

はじめに:

プロキシパターンの内部原理、役割、およびリモートプロキシの実装については前の記事で詳しく説明しました。この記事ではその内容を補足し、他のプロキシについて紹介します。

一.仮想プロキシ

まず、仮想プロキシの役割を明確にしましょう:巨大なオブジェクトが実際に作成される前に、仮想プロキシが巨大なオブジェクトの代わりを務めます(巨大オブジェクトが必要なときにのみ作成されることを保証し、リソースの無駄を避けます)。仮想プロキシは実際によく使っているかもしれませんが、それを認識していないだけです。例えば:

プログラム内に密な計算やサーバーから大量のデータを取得するなどの時間とリソースを消費する操作が存在する場合、通常はまずデフォルト情報を表示するか、プログラムがまだ努力して作業中であることを示すアニメーションを表示し、操作完了後に取得した情報を表示します。ここで仮想プロキシの思想が使われていますが、仮想プロキシは操作完了を待つ間の作業をすべて分離して仮想プロキシオブジェクトにカプセル化し、クライアントと直接対話するのは仮想プロキシであって実際のオブジェクトではありません。

仮想プロキシはクライアントの要求を受け取った後、以下のことを行います:

  1. 実際のオブジェクトが存在しない場合(まだ作成されていない、または作成が完了していない)、クライアントの要求を記録し、デフォルト情報を表示します。実際のオブジェクトが作成完了した後、要求を実際のオブジェクトに渡します。
  2. 実際のオブジェクトが既に存在する場合、クライアントの要求を実際のオブジェクトに委譲します。
  3. 実際のオブジェクトが操作結果を返す前に、プロキシオブジェクトがデフォルト情報の表示(またはデフォルト処理)を担当します。
  4. 操作結果を得た後、直接クライアントに渡します(そうです、プロキシの任務は「渡す」と「受け取る」です)。

プロキシである以上、実際のオブジェクトのように振る舞わなければならず、クライアントにプロキシの存在を気づかせてはいけません。したがって、仮想プロキシのクラス図は以下のようになります:

Client は仮想プロキシオブジェクト Proxy を保持し、Proxy は具体的なサービスオブジェクト MyService を保持します。

さらに重要なのは:仮想プロキシ Proxy と具体的なサービス MyService はどちらも Service インターフェースを拡張しているため、プロキシと実際のオブジェクトは同じ振る舞いを持ち、クライアントは Proxy の存在に気づきません。

二.保護プロキシ

保護プロキシはマネージャーの役割を果たします(誰もが背後にいるスターに連絡できるわけではありません。彼らに連絡するのは私だけです。あなたが悪い人ではないことを確認した後、転接します。。)

言い換えれば、保護プロキシは実際のオブジェクトへのアクセス制御を提供し、正当な呼び出し元のみがサービスを得られることを保証します。

実装については、クラス図は上の仮想プロキシと全く同じですが、Proxy には呼び出し元の権限を判断する責任が追加されます(何か気づきましたか?複数のプロキシを組み合わせることができるようです。もちろん、そのようなプロキシはクラスの単一責任原則を満たしませんが、没关系、一連のプロキシを管理するプロキシのプロキシを作成すれば。。)

実際、Java の API はプロキシパターンへのサポートを提供しています(動的プロキシ)。Java が提供する動的プロキシの「動的」は、Proxy プロキシクラス(そうです、オブジェクトではなくクラスです)が実行時にのみ作成される点に現れています。Proxy の内部実装を直接変更することはできませんが、InvocationHandler を通じて Proxy に何をするべきかを伝えることができます。

動的プロキシを使用して保護プロキシを実装する基本的な考え方は:複数のプロキシを作成し、異なるプロキシが異なる権限の呼び出し元に対応するサービスを提供することです。

では、動的プロキシを使用して保護プロキシを実装するシナリオを設計してみましょう:ブログ記事のいいねメカニズム:他人のブログ記事にはいいねできるが、自分のブログ記事にはいいねできない。ただし、ブロガーと訪問者はいいね数を見ることができます。

シナリオは単純ですが、問題を説明するには十分です。以下で動的プロキシによる保護プロキシの実装をシミュレートします。まず BlogUser が必要です:

package ProxyPattern.ProtectionProxy;

/**

  • 定義博客用户接口
  • @author ayqy */ public interface BlogUser { public void support(); public int getSupportCount(); }

ConcreteUser も必要ですが、その実装は比較的単純なのでここでは展開しません。以下で動的プロキシの作成を開始します。ここでは 2 つの権限、ブロガーと訪問者があるため、少なくとも 2 つの異なる動的プロキシが必要です。

AuthorHandler:

package ProxyPattern.ProtectionProxy;

import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;

/**

  • 实现为作者服务的 InvocationHandler

  • @author ayqy */ public class AuthorHandler implements InvocationHandler{ BlogUser user;

    public AuthorHandler(BlogUser user){ this.user = user; }

    @Override public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {//声明非法访问异常 try { /如果博主想要查看被赞数目,则委托给调用者 user 实现具体操作/ if(method.getName().equals("getSupportCount")){ return method.invoke(user, args); } /如果博主要给自己点赞,则抛出异常表示拒绝/ if(method.getName().equals("support")){ throw new IllegalAccessException(); } } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); }

     	return null;//如果调用其它方法,直接无视
    

    }

}

これと同様に、VisitorHandler も実装する必要があります。複数のプロキシ有了ので、プロキシのプロキシ、つまり保護プロキシ(呼び出し元の権限に応じて対応する動的プロキシを返す)を実装する必要があります。

ProtectionProxy:

package ProxyPattern.ProtectionProxy;

import java.lang.reflect.Proxy;

/**

  • 实现保护代理

  • @author ayqy / public class ProtectionProxy { /*

    • @param user 调用者
    • @return 作者对应的代理 */ public BlogUser getAuthorProxy(BlogUser user){ return (BlogUser)Proxy.newProxyInstance(user.getClass().getClassLoader() , user.getClass().getInterfaces() , new AuthorHandler(user)); }

    /**

    • @param user 调用者
    • @return 游客对应的代理 */ public BlogUser getVisitorProxy(BlogUser user){ return (BlogUser)Proxy.newProxyInstance(user.getClass().getClassLoader() , user.getClass().getInterfaces() , new VisitorHandler(user)); }

    /在此处插入生成其它代理的方法/ }

これだけで十分です。簡単なテストを行います:

package ProxyPattern.ProtectionProxy;

/**

  • 实现测试类

  • @author ayqy */ public class Test { public static void main(String[] args){ //创建保护代理 ProtectionProxy proxy = new ProtectionProxy();

     System.out.println("博主部分:");
     //创建博主对应的代理
     BlogUser authorProxy = proxy.getAuthorProxy(new ConcreteUser());
     System.out.println("博主要查看被赞总数");
     System.out.println("被赞 " + authorProxy.getSupportCount() + " 次");
     //博主要给自己点赞
     System.out.println("博主要给自己点赞");
     try{
     authorProxy.support();
     }catch(Exception e){
     	System.out.println("点赞失败,无法给自己点赞");
     }
     System.out.println("自赞之后,被赞 " + authorProxy.getSupportCount() + " 次");
    
     
     System.out.println("\n游客部分:");
     //创建游客对应的代理
     BlogUser vistorProxy = proxy.getVisitorProxy(new ConcreteUser());
     System.out.println("游客要查看被赞总数");
     System.out.println("被赞 " + vistorProxy.getSupportCount() + " 次");
     //博主要给自己点赞
     System.out.println("游客要给博主点赞");
     try{
     vistorProxy.support();
     }catch(Exception e){
     	System.out.println("点赞失败,原因未知");
     }
     System.out.println("点赞之后,被赞 " + vistorProxy.getSupportCount() + " 次");
    

    } }

テスト結果:

三.その他のプロキシ

プロキシパターンは広く応用されており、すべてのプロキシを一つ一つ挙げることはできません。ここでは一般的なプロキシをいくつか簡単に紹介します:

プロキシ名役割
ファイアウォールプロキシネットワークリソースへのアクセスを制御し、「悪いクライアント」からの侵害から主題を保護します
スマート参照プロキシ主題が参照されるときに追加のアクションを実行します。例えば、オブジェクトが参照された回数を計算します
キャッシュプロキシオーバーヘッドの大きい演算結果に一時的なストレージを提供し、複数のクライアントが結果を共有できるようにして、計算またはネットワーク遅延を削減します
同期プロキシマルチスレッドの状況下で主題への安全なアクセスを提供します
複雑隠蔽プロキシ(ファサードプロキシ)クラスの複雑なコレクションの複雑さを隠蔽し、アクセス制御を行います(ファサードパターンよりも強力な機能を持ちます)
コピーオンライトプロキシオブジェクトのコピーを制御します。方法は、実際にコピーが必要になるまでオブジェクトのコピーを遅延させることです(仮想プロキシに似ています)

もちろん、最も元々のリモートプロキシを理解していれば、これらのプロキシに出会ったときも簡単に理解できます。。

コメント

コメントはまだありません

コメントを書く