1. 程式人生 > 實用技巧 >結構型 - 代理模式

結構型 - 代理模式

1、代理模式

  • 定義:

    給某一個物件提供一個代理物件,並由代理物件控制對原物件的引用。

    就好像房東想要出租房子,他讓中介幫忙找租客,帶看,籤合同,房東只需要把房子掛出去表示出租就行了

  • 為什麼要使用代理模式?

    • 中介隔離作用:

      • 在某些情況下,一個客戶不想或者不能去直接訪問真實角色,代理角色就能在中間起到一個橋樑連線的作用,前提條件是真實角色委託了代理角色,他們實現相同的介面,有共同的目標。

    • 開閉原則:

      • 代理角色還可以增加額外的功能,這樣做我們只需要修改代理角色類而不需要再修改真實角色類,符合程式碼設計的開閉原則

      • 代理角色類主要負責為真實角色類【預處理訊息、過濾訊息、把訊息轉發給真實角色,以及事後對返回結果的處理等

      • 代理角色類本身並不真正實現服務,而是同過呼叫真實角色類的相關方法,來提供特定的服務。真正的業務功能還是由真實角色類來實現,但是可以在業務功能執行的前後加入一些公共的服務,如:快取、日誌這些功能,在代理角色類中編寫,不用開啟封裝好的真實角色類。

2、靜態代理

  • 定義:

    靜態代理是由程式設計師建立或特定工具自動生成原始碼,在對其編譯。在程式設計師執行之前,代理類.class檔案就已經被建立了

  • 示例一:租房

    • Rent(租房事件)

      public interface Rent {
      public void rent();
      }
    • Host(房東:出租房子)

      public class Host implements Rent {
      public void rent() {
      System.out.println("房東出租房子!");
      }
      }
    • HousingAgency(房產中介:代理房東租房子)

      public class HousingAgency implements Rent {
      public Host host;

      ////通過構造方法實現房東與中介的委託代理
      public HousingAgency(Host host) {
      this.host = host;
      }

      public void rent() {
      host.rent();
      signContract();
      seeHouse();
      agencyFee();
      }

      public void signContract() {
      System.out.println("籤合同");
      }

      public void seeHouse() {
      System.out.println("中介帶看");
      }

      public void agencyFee() {
      System.out.println("中介收取中介費");
      }
      }
    • Tenant(租客:租房子)

      public class Tenant {

      public static void main(String[] args) {

      //例項化一個房東,生成委託物件
      Host host = new Host();
      //房產中介代理房東出租房子
      HousingAgency agency = new HousingAgency(host);
      /*
      租客通過中介租房子,中介也可以實現其他的功能
      輸出結果:
      房東出租房子!
      籤合同
      中介帶看
      中介收取中介費
      */
      agency.rent();

      }
      }
  • 示例二:使用靜態代理實現日誌功能

    • UserService

      public interface UserService {
      public void add();
      public void delete();
      public void update();
      public void query();
      }
    • UserServiceImpl

      public class UserServiceImpl implements UserService {
      public void add() {
      System.out.println("實現了add方法");
      }

      public void delete() {
      System.out.println("實現了delete方法");
      }

      public void update() {
      System.out.println("實現了update方法");
      }

      public void query() {
      System.out.println("實現了query方法");
      }
      }
    • UserServiceProxy

      public class UserServiceProxy implements UserService {

      private UserServiceImpl userService;

      //通過set方法實現userServiceImpl的委託代理
      public void setUserService(UserServiceImpl userService) {
      this.userService = userService;
      }

      public void add() {
      log("add");
      userService.add();
      }

      public void delete() {
      log("delete");
      userService.delete();
      }

      public void update() {
      log("update");
      userService.update();
      }

      public void query() {
      log("query");
      userService.query();
      }

      public void log(String msg) {
      System.out.println("[DEBUG] 呼叫了" + msg + "方法");
      }
      }
    • Client

      public class Client {
      public static void main(String[] args) {

      UserServiceImpl userService = new UserServiceImpl();
      UserServiceProxy proxy = new UserServiceProxy();
      proxy.setUserService(userService);

      /*
      輸出結果:
      [DEBUG] 呼叫了add方法
      實現了add方法
      */
      proxy.add();
      /*
      輸出結果:
      [DEBUG] 呼叫了query方法
      實現了query方法
      */
      proxy.query();
      }
      }
  • 優點:

    • 可以使真實角色的操作更加純粹,不用去關注公共業務

    • 代理角色負責公共業務,分工更加明確

    • 公共業務發生擴充套件的時候,方便集中管理

  • 缺點:

    • 一個真實角色就會產生一個代理角色,程式碼量會增加,開發效率降低。

3、動態代理

  • 定義:

    動態代理是在程式執行時通過反射機制動態建立的

  • 類別:

    • 基於介面的,JDK動態代理

      • Proxy

      • InvocationHandler

    • 基於類的

      • cglib

    • Java位元組碼實現

      • javaSsist

  • 示例一:租房

    • Rent

      public interface Rent {
      public void rent();
      }
    • Host

      public class Host implements Rent {
      public void rent() {
      System.out.println("房東出租房子!");
      }
      }
    • ProxyInvocationHandler(代理呼叫處理程式)

      //這是個可以自動生成代理類的類
      public class ProxyInvocationHandler implements InvocationHandler {

      //被代理的類
      private Rent rent;

      public void setRent(Rent rent) {
      this.rent = rent;
      }

      //生成代理類
      //第一個引數獲得類載入器,
      //第二個引數獲得被代理類的介面
      //第三個引數是InvocationHandler,因為這個類實現了InvocationHandler,所以可以是它本身
      public Object getProxy() {
      return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
      }

      //處理代理例項,並返回結果
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      //用反射機制來實現動態代理的本質
      Object result = method.invoke(rent, args);
      signContract();
      seeHouse();
      agencyFee();
      return result;
      }

      public void signContract() {
      System.out.println("籤合同");
      }

      public void seeHouse() {
      System.out.println("中介帶看");
      }

      public void agencyFee() {
      System.out.println("中介收取中介費");
      }
      }
    • Tenant

      public class Tenant {
      public static void main(String[] args) {

      Host host = new Host();
      ProxyInvocationHandler proxyIH = new ProxyInvocationHandler();

      /*
      每個代理例項都有一個關聯的呼叫處理程式,
      當在代理例項上呼叫方法時,方法呼叫將被編碼並且分配到其呼叫處理程式的invoke方法。
      通過呼叫處理程式來處理我們要呼叫的介面物件
      */
      proxyIH.setRent(host);

      //動態生成代理角色
      Rent proxy = (Rent) proxyIH.getProxy();
      //代理角色去做業務
      proxy.rent();
      }
      }
  • 示例二:使用動態代理實現日誌功能

    • UserService

      public interface UserService {

      public void add();
      public void delete();
      public void update();
      public void query();
      }
    • UserServiceImpl

      public class UserServiceImpl implements UserService {

      public void add() {
      System.out.println("實現了add方法");
      }

      public void delete() {
      System.out.println("實現了delete方法");
      }

      public void update() {
      System.out.println("實現了update方法");
      }

      public void query() {
      System.out.println("實現了query方法");
      }
      }
    • ProxyInvocationHandler

      //這是個可以自動生成代理類的工具類
      public class ProxyInvocationHandler implements InvocationHandler {

      //被代理的類
      private Object target;

      public void setTarget(Object target) {
      this.target = target;
      }

      //生成代理類
      //第一個引數獲得類載入器,
      //第二個引數獲得被代理類的介面
      //第三個引數是InvocationHandler,因為這個類實現了InvocationHandler,所以可以是它本身
      public Object getProxy() {
      return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
      }

      //處理代理例項,並返回結果
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      //用反射來獲取執行方法的名字
      log(method.getName());
      //用反射機制來實現動態代理的本質
      Object result = method.invoke(target, args);
      return result;
      }

      public void log(String msg) {
      System.out.println("[DEBUG] 執行了" + msg + "方法!");
      }
      }
    • Client

      public class Client {
      public static void main(String[] args) {
      //真實角色
      UserServiceImpl userService = new UserServiceImpl();

      ProxyInvocationHandler proxyIHandler = new ProxyInvocationHandler();
      //設定要代理的物件
      proxyIHandler.setTarget(userService);
      //動態生成代理類
      UserService proxy = (UserService) proxyIHandler.getProxy();
      proxy.delete();
      }
      }
  • 優點:

    • 可以使真實角色的操作更加純粹,不用去關注公共業務

    • 代理角色負責公共業務,分工更加明確

    • 公共業務發生擴充套件的時候,方便集中管理

    • 一個動態代理類代理的是一個介面,一般對應的就是一類業務

    • 一個動態代理類可以代理多個實現了同一個介面的類