1. 程式人生 > >設計模式之結構性模式(一)

設計模式之結構性模式(一)

設計模式之結構性模式(一)

一、介面卡模式
二、代理模式

核心作用:從程式的結構上實現鬆耦合,從而可以擴大整體的類結構,解決更大的問題
分類:介面卡模式、代理模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式

一、介面卡模式

將一個類的介面轉換成客戶希望的另一個介面,可以使原本由於介面不相容而不能一起工作的類可以一起工作

模式中的角色:

目標介面(Target):客戶所期待的介面,目標可以是具體的或抽象的類,也可以是介面
需要適配的類(Adaptee):需要適配的類或適配者的類
介面卡(Adapter):通過包裝一個需要適配的物件,把原介面轉換成目標介面
在這裡插入圖片描述

例項:電腦(Client) 轉換器(Adapter) 鍵盤(Adaptee)

Adaptee

/**
 * 被適配的類(相當於鍵盤)
 */
package com.adapter;

public class Adaptee {
    //需求
    public void request() {
    	System.out.println("可以完成客戶端請求需要的功能");
    }

}

Adapter(類介面卡)

/**
 * 介面卡(相當於轉接器,USB)
 */
package com.adapter;

public class Adapter extends Adaptee implements Target{
@Override
public void handleRequest() {
    super.request();
    }
}

Target

//目標介面
package com.adapter;

public interface Target {
//處理需求的方法
public void handleRequest();
}

Adapter2(物件是介面卡)

/**
 * 介面卡(相當於轉接器)
 */
package com.adapter;

public class Adapter2 implements Target {
    Adaptee adaptee;
    
    public Adapter2(Adaptee adaptee) {
    super();
    this.adaptee = adaptee;
}

@Override
public void handleRequest() {
    adaptee.request();
 }
}

Client

/**
 * 客戶端類(相當於筆記本,只有USB介面)
 */
package com.adapter;

public class Client {

public void test(Target t) {
	t.handleRequest();
}

public static void main(String[] args) {
    Client laptop = new Client();//電腦(客戶端)
    Adaptee keyboard = new Adaptee();//鍵盤(適配的類)
    //Target adapter = new Adapter();//介面卡(轉換器)
    Target adapter = new Adapter2(keyboard);//介面卡(轉換器)
    laptop.test(adapter);
    }
}

結果:

可以完成客戶端請求需要的功能

二、代理模式

核心作用:通過代理,控制對物件的訪問
可以詳細控制訪問某個物件的方法,在呼叫這個方法前做前置處理,呼叫這個方法後做後置處理(AOP的微觀實現)
AOP:Aspect Oriented Progrmming 面向切面程式設計的核心機制

核心角色:
抽象角色:定義代理角色和真實角色的公共對外方法
真實角色:實現抽象角色,定義真實角色所要實現的業務邏輯,供代理角色呼叫(關注真正的業務邏輯)
代理角色:實現抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,並可以附加自己的操作(將統一的流程控制放到代理角色中處理)
在這裡插入圖片描述

應用場景:
安全代理:遮蔽對真實角色的直接訪問
遠端代理:通過代理類處理遠端方法呼叫(RMI)
延時載入:先載入輕量級的代理物件,真正需要再載入真實物件
(比如開發一個大文件檢視軟體,大文件中有大的圖片,有可能一個圖片有100MB,在開啟檔案時,不可能將所有檔案都顯示出來,這樣就可以使用代理模式,當需要檢視圖片時,用proxy來進行大圖片的開啟)

分類:
靜態代理(靜態定義代理類)
動態代理(動態定義代理類)
1)JDK自帶的動態代理
2)javaassist位元組碼操作庫實現
3)CGLIB
4)ASM(底層使用指令,可維護性較差)

靜態代理類

例項:明星 明星代理人

Star

/**
 * 明星介面
 */
package com.proxy.staticProxy;

public interface Star {
    public void confer();//面談
    public void signContract();//籤合同
    public void bookTicket();//訂機票
    public void sing();//唱歌
    public void collectMoney();//收錢
}

RealStar

/**
 * 明星本人
 */
package com.proxy.staticProxy;

public class RealStar implements Star {
    @Override
    public void confer() {
    System.out.println("RealStar.confer()");
}

@Override
    public void signContract() {
    System.out.println("RealStar.signContract()");
}

@Override
    public void bookTicket() {
    System.out.println("RealStar.bookTicket()");
}

@Override
    public void sing() {
    System.out.println("RealStar.sing()(明星本人)");
}

@Override
public void collectMoney() {
    System.out.println("RealStar.collectMoney()");
    }
}

ProxyStar

/**
 * 明星代理人
 */
package com.proxy.staticProxy;

public class ProxyStar implements Star {

    @Override
    public void confer() {
    	System.out.println("ProxyStar.confer()");
    }
    
    @Override
    public void signContract() {
    	System.out.println("ProxyStar.signContract()");
    }
    
    @Override
    public void bookTicket() {
   		System.out.println("ProxyStar.bookTicket()");
    }
    
    @Override
    public void sing() {
  	  System.out.println("RealStar.sing()");
    }
    
    @Override
    public void collectMoney() {
  	  System.out.println("ProxyStar.collectMoney()");
    }
}

Client

/**
 * 測試函式
 */
package com.proxy.staticProxy;

public class Client {
    public static void main(String[] args) {
	    Star realStar = new RealStar();
	    Star proxyStar = new ProxyStar();
	    
	    proxyStar.confer();
	    proxyStar.bookTicket();
	    proxyStar.signContract();
	    proxyStar.collectMoney();
	    proxyStar.sing();
    }
}

結果

ProxyStar.confer()
ProxyStar.bookTicket()
ProxyStar.signContract()
ProxyStar.collectMoney()
RealStar.sing()//注意

動態代理類

JDK自帶的動態代理

在這裡插入圖片描述

Star

RealStar

StarHandler

package dynasticProxy;

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

public class StarHandler implements InvocationHandler {//處理器介面

    //建立真實的明星
    Star realStar = null;
    
    //構造器
    public StarHandler(Star realStar) {
	    super();
	    this.realStar = realStar;
    }
    
    //通過invoke()方法實現對真實角色的代理訪問
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	    Object object = null;
	    
	    System.out.println("真正的方法執行前");
	    System.out.println("面談、籤合同、待付款、訂機票");
	    
	    //如果是sing,就呼叫realStar()方法
	    if(method.getName().equals("sing")) {
	   	 object = method.invoke(realStar, args);
	    }
	    
	    System.out.println("真正的方法執行後");
	    System.out.println("收尾款");
	    return object;
    }
}

Client

package dynasticProxy;

import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
	    Star realStar = new RealStar();
	    StarHandler handler = new StarHandler(realStar);
	    
	    //每次通過Proxy生成代理類處理物件時,都要指定對應的處理器物件
	    Star proxy = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Star.class} ,handler );
	    
	    proxy.sing();
    }
}

結果:

真正的方法執行前
面談、籤合同、待付款、訂機票
RealStar.sing()(明星本人)
真正的方法執行後
收尾款

代理模式開發框架中應用場景:

資料庫連線池關閉處理
mybatis中實現攔截器外掛
Aspect的實現
String中AOP的實現(日誌攔截、宣告式事務處理)
web service
RMI遠端方法呼叫

面向切面程式設計常用術語:

切面:共有功能的實現
通知:切面的具體實現
連線點:程式在執行過程中能夠插入切面的地點
切入點:用於定義應該切入到那些連線點上
目標物件:即將切入切面的物件,即被通知的物件
代理物件L:通知應用到目標物件之後被動態建立的物件
織入:將切面應用到目標物件從而建立一個新的代理物件的過程