1. 程式人生 > >結構型設計模式

結構型設計模式

owa package ret public sub intercept pro eth 磁盤

架構型設計模式成員
  • 門面模式
  • 代理模式
  • 裝飾器模式
  • 組合模式
  • 享元模式
  • 橋接模式
  • 適配器模式

1. 代理模式

1.1 定義

為其他對象提供一種代理以控制對這個對象的訪問

解決問題:在直接訪問對象時帶來的問題,比如說:要訪問的對象在遠程的機器上。在面向對象系統中,有些對象由於某些原因(比如對象創建開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此對象時加上一個對此對象的訪問層

1.2 分類

1.2.1 靜態代理

  • 一個代理類只能對一個業務接口的實現類進行包裝,如果有多個業務接口的話就要定義很多實現類和代理類才行
  • 而且,如果代理類對業務方法的預處理、調用後操作都是一樣的(比如:調用前輸出提示、調用後自動關閉連接),則多個代理類就會有很多重復代碼
接口類

package com.zhunongyun.spring.proxy;

public interface Image {
   void display();

   void change(String imagePath);
}

--------------------------------------------------

接口的實現類

package com.zhunongyun.spring.proxy;

public class RealImage implements Image {

   private String fileName;

   public RealImage(String fileName){
      this.fileName = fileName;
      loadFromDisk(fileName);
   }

   @Override
   public void display() {
      System.out.println("顯示圖片: " + fileName);
   }

   @Override
   public void change(String imagePath) {
      System.out.println("替換圖片: " + imagePath);
   }

   private void loadFromDisk(String fileName){
      System.out.println("加載圖片: " + fileName);
   }
}

-------------------------------------------------------

代理類

package com.zhunongyun.spring.proxy;

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();
   }

   @Override
   public void change(String imagePath) {
      if(realImage == null){
         realImage = new RealImage(fileName);
      }
      realImage.change(imagePath);
   }
}

---------------------------------------------------

測試類

package com.zhunongyun.spring.proxy;

public class ProxyPatternDemo {

   public static void main(String[] args) {
      Image image = new ProxyImage("test_10mb.jpg");

      // 圖像將從磁盤加載
      image.display(); 
      System.out.println("---------------------");
      // 圖像不需要從磁盤加載
      image.display();  
   }
}

輸出結果:
加載圖片: test_10mb.jpg
顯示圖片: test_10mb.jpg
---------------------
顯示圖片: test_10mb.jpg

1.2.2 動態代理

1.2.2.1 JDK 自帶的動態代理

  • java.lang.reflect.Proxy: 生成動態代理類和對象
  • java.lang.reflect.InvocationHandler(處理器接口):可以通過invoke方法實現對真實角色的代理訪問

每次通過 Proxy 生成的代理類對象都要指定對應的處理器對象

接口類

package com.zhunongyun.spring.proxy.dynamic;

public interface Subject {
    int sellBooks();

    String speak();
}

---------------------------------------------------------

接口實現類

package com.zhunongyun.spring.proxy.dynamic;

public class RealSubject implements Subject{
    @Override
    public int sellBooks() {
        System.out.println("賣書");
        return 1 ;
    }

    @Override
    public String speak() {
        System.out.println("說話");
        return "張三";
    }
}

------------------------------------------------------------------

動態代理類

package com.zhunongyun.study.toalibaba.spring.proxy.dynamic;

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

/**
 * 定義一個處理器
 *
 * @author gnehcgnaw
 * @date 2018/11/5 19:26
 */
public class MyInvocationHandler implements InvocationHandler {
    /**
     * 這其實業務實現類對象,用來調用具體的業務方法
     */
    private Object target;

    /**
     * 綁定業務對象並返回一個代理類
     */
    public Object bind(Object target) {
        //接收業務實現類對象參數
        this.target = target;

        //通過反射機制,創建一個代理類對象實例並返回。用戶進行方法調用時使用
        //創建代理對象時,需要傳遞該業務類的類加載器(用來獲取業務實現類的元數據,在包裝方法是調用真正的業務方法)、接口、handler實現類
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    /**
     * @param proxy  代理類
     * @param method 正在調用的方法
     * @param args   方法的參數
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Object result=null;

        System.out.println("預處理操作——————");
        //調用真正的業務方法  
        result=method.invoke(target, args);

        System.out.println("調用後處理——————");
        return result;
    }
}

-------------------------------------------------------------------

測試類

package com.zhunongyun.spring.proxy.dynamic;

import java.lang.reflect.Proxy;

/**
 * 調用類
 * @author gnehcgnaw
 * @date 2018/11/7 20:26
 */
public class Client {
    public static void main(String[] args) {
        //真實對象
        Subject realSubject =  new RealSubject();

        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(realSubject);
        //代理對象
        Subject proxyClass = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Subject.class}, myInvocationHandler);

        proxyClass.sellBooks();

        proxyClass.speak();
    }
}

------------------------------------------------------------------------
輸出結果:

預處理操作——————
賣書
調用後處理——————
預處理操作——————
說話
調用後處理——————

1.2.2.2 CGlib動態代理

操作類

package com.zhunongyun.study.toalibaba.spring.proxy.cglib;

public class BookFacadeImpl {
    public void addBook() {  
        System.out.println("新增圖書...");  
    }  
}  

-------------------------------------------

CGlib代理類

package com.zhunongyun.study.toalibaba.spring.proxy.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class BookFacadeCglib implements MethodInterceptor {
    /**
     * 業務類對象,供代理方法中進行真正的業務方法調用
     */
    private Object target;

    /**
     * 相當於JDK動態代理中的綁定
     * @param target
     * @return
     */
    public Object getInstance(Object target) {
        //給業務對象賦值
        this.target = target;

        //創建加強器,用來創建動態代理類
        Enhancer enhancer = new Enhancer();

        //為加強器指定要代理的業務類(即:為下面生成的代理類指定父類)
        enhancer.setSuperclass(this.target.getClass());

        //設置回調:對於代理類上所有方法的調用,都會調用CallBack,而Callback則需要實現intercept()方法進行攔
        enhancer.setCallback(this);

        // 創建動態代理類對象並返回
        return enhancer.create();
    }

    /**
     * 實現回調方法
     * @param obj
     * @param method
     * @param args
     * @param proxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("預處理——————");

        //調用業務類(父類中)的方法
        proxy.invokeSuper(obj, args);
        System.out.println("調用後操作——————");
        return null;
    }
}

------------------------------------------------

測試類

package com.zhunongyun.study.toalibaba.spring.proxy.cglib;

public class CglibDemo {
    public static void main(String[] args) {
        BookFacadeImpl bookFacade = new BookFacadeImpl();

        BookFacadeCglib cglib = new BookFacadeCglib();

        BookFacadeImpl bookCglib = (BookFacadeImpl) cglib.getInstance(bookFacade);

        bookCglib.addBook();
    }
}

----------------------------------------

輸出結果

預處理——————
新增圖書...
調用後操作——————

1.2.2.3 JDK動態代理與CGlib動態代理

  • JDK動態代理是通過接口中的方法名,在動態生成的代理類中調用業務實現類的同名方法
  • CGlib動態代理是通過繼承業務類,生成的動態代理類是業務類的子類,通過重寫業務方法進行代理

結構型設計模式