結構型模式(Structural Pattern)用於將類或對象結合在一塊兒造成更強大的結構,就像搭積木,能夠經過簡單的積木組合出複雜、功能強大的模型。java
結構型模式 | 重要程度 |
---|---|
適配器模式(Adapter) | ⭐⭐⭐⭐ |
橋接模式(Bridge) | ⭐⭐⭐ |
組合模式(Composite) | ⭐⭐⭐⭐ |
裝飾者模式(Decorator) | ⭐⭐⭐ |
外觀模式(Facade) | ⭐⭐⭐⭐⭐ |
享元模式(Flyweight) | ⭐ |
代理模式(Proxy) | ⭐⭐⭐⭐ |
生活中,充電插頭有兩腳的、三腳的,還有圓形的,若是想使這些插頭都能工做,就須要一個多功能適配器git
適配器模式(Adapter Pattern)屬於結構性模式,它能夠將某個類的接口轉換爲客戶端指望的另外一個接口表示,主要目的是兼容性,讓本來因接口不匹配不能一塊兒工做的兩個類能夠協同工做,其別名爲包裝器(Wrapper)。適配器模式主要分爲三類:類適配器模式、對象適配器模式、接口適配器模式。github
Adapter 類繼承 src 類,實現 dst 接口,完成 src 對 dst 的適配。web
插座(Voltage220V)的輸出電壓是220V,充電插頭(Voltage5V)輸出電壓是5V,這時候就須要一個適配器(VoltageAdapter)轉換電壓,才能給手機(Phone)充電數據庫
電源輸出電壓爲220V編程
public class Voltage220V { public int output220V() { int src = 220; System.out.println("電源輸出" + src + "V"); return src; } }
充電器輸出電壓爲5V設計模式
public interface Voltage5V { int output5V(); }
適配器須要將220V轉爲5V數組
public class VoltageAdapter extends Voltage220V implements Voltage5V { @Override public int output5V() { int src = super.output220V(); int dst = src / 44; System.out.println("轉換爲" + dst + "V"); return dst; } }
手機接收5V電壓,判斷電壓是否爲5V緩存
public class Phone { public static void charging(Voltage5V voltage5V){ int v = voltage5V.output5V(); if(v == 5){ System.out.println("接收電壓爲5V,正常充電"); }else if(v > 5){ System.out.println("電壓高於5V,沒法充電"); } } }
測試方法安全
@Test public void test01(){ System.out.println("====類適配器模式===="); Phone.charging(new VoltageAdapter()); }
運行結果
====類適配器模式==== 電源輸出220V 轉換爲5V 接收電壓爲5V,正常充電
基本的思路和類的適配器模式相同,只是將 Adapter 類作修改,使用聚合關係替代繼承關係
沿用前面的代碼,新建一個適配器,只是將原來的 Adapter 繼承 src 類換爲聚合的關係
public class VoltageAdapter2 implements Voltage5V { private Voltage220V voltage220V; public VoltageAdapter2(){ this.voltage220V = new Voltage220V(); } @Override public int output5V() { int src = this.voltage220V.output220V(); int dst = src / 44; return dst; } }
測試方法
@Test public void test02(){ System.out.println("====對象適配器模式===="); Phone.charging(new VoltageAdapter2(new Voltage220V())); }
運行結果
====對象適配器模式==== 電源輸出220V 轉換爲5V 接收電壓爲5V,正常充電
接口適配器模式也可稱爲缺省適配器模式,當不須要實現接口的所有方法時,可先設計一個抽象類實現接口,併爲該接口的每一個方法都提供一個默認實現,那麼該抽象類的子類就能夠有選擇的覆蓋父類的某些方法來實現需求。
適用於一個接口不想使用其全部的方法的狀況
寫一個接口,裏面定義一些方法
public interface InterfaceMethod { void m1(); void m2(); void m3(); void m4(); }
一個抽象類,實現該接口
public abstract class AbstractAdapter implements InterfaceMethod { @Override public void m1() { } @Override public void m2() { } @Override public void m3() { } @Override public void m4() { } }
測試方法
@Test public void test(){ //使用匿名內部類的方式 AbstractAdapter adapter = new AbstractAdapter() { @Override public void m1() { System.out.println("我要用m1方法"); } }; adapter.m1(); }
運行結果
我要用m1方法
類適配器:以類給到,在 Adapter 裏,就是將 src 當作類,繼承
對象適配器:以對象給到,在 Adapter 裏, 將 src 做爲一個對象,持有
接口適配器:以接口給到,在 Adapter 裏,將 src 做爲一個接口,實現
橋接模式包含以下角色:
咱們以手機爲例,手機有品牌(諾基亞、摩托羅拉)和樣式(摺疊式、直立式),咱們須要生產不一樣的品牌和樣式,好比摺疊式諾基亞、直立式摩托羅拉... ...
「實現類接口」 - 手機品牌,都有開機和關機的功能
public interface PhoneBrand { void open(); void close(); }
「具體實現類」 - 手機品牌 Nokia 和 Moto
public class Nokia implements PhoneBrand { @Override public void open() { System.out.println("諾基亞開機..."); } @Override public void close() { System.out.println("諾基亞關機..."); } }
public class Moto implements PhoneBrand { @Override public void open() { System.out.println("摩托羅拉開機..."); } @Override public void close() { System.out.println("摩托羅拉關機..."); } }
「抽象類」 - 手機類,以聚合的方式與品牌產生聯繫,充當着「橋」的角色
public abstract class AbsPhone{ private PhoneBrand brand; public AbsPhone(PhoneBrand brand) { this.brand = brand; } protected void open(){ brand.open(); } protected void close(){ brand.close(); } }
「擴充抽象類」 - 摺疊式手機 和 直立式手機
public class FoldingPhone extends AbsPhone{ public FoldingPhone(PhoneBrand brand) { super(brand); } @Override protected void open() { System.out.print("摺疊式 - "); super.open(); } @Override protected void close() { System.out.print("摺疊式 - "); super.close(); } }
public class UpRightPhone extends AbsPhone{ public UpRightPhone(PhoneBrand brand) { super(brand); } @Override protected void open() { System.out.print("直立式 - "); super.open(); } @Override protected void close() { System.out.print("直立式 - "); super.close(); } }
測試
@Test public void test(){ AbsPhone p1 = new FoldingPhone(new Nokia()); p1.open(); p1.close(); System.out.println(); AbsPhone p2 = new UpRightPhone(new Moto()); p2.open(); p2.close(); }
結果
摺疊式 - 諾基亞開機... 摺疊式 - 諾基亞關機... 直立式 - 摩托羅拉開機... 直立式 - 摩托羅拉關機...
若是咱們想建立其餘類型的手機,只須要改變建立方式便可。
在 Java 中咱們一般使用 JDBC 鏈接數據庫,可是數據庫的種類有不少(MySQL、Oracle...),它們的鏈接方式、協議都不盡相同,很顯然不能爲每種數據庫都寫一個接口,這樣就違背了精簡設計原則,因而Java設計師就提供一套接口給廠商們本身實現,一套接口給用戶調用。
咱們在使用 JDBC 的時候須要寫這樣的代碼
Class.forName("數據庫驅動名"); Connection conn = DriverManager.getConnection("數據庫url", "用戶名", "密碼");
其過程是這樣的:
Class.forName()
的時候,經過反射機制,將 .class
文件加載進Java虛擬機內存中,Driver
類初始化,執行如下代碼,向 DriverManager
中註冊一個驅動。DriverManager
是個 Driver
容器,管理不一樣的 Driver
static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } }
咱們獲取鏈接時,DriverManager
就會根據驅動返回一個相應的數據庫鏈接
@CallerSensitive public static Connection getConnection(String url, java.util.Properties info) throws SQLException { return (getConnection(url, info, Reflection.getCallerClass())); }
對於那些不但願使用繼承或由於多層次繼承致使系統類的個數急劇增長的系統,橋接模式尤其適用。
一、組合模式(Composite Pattern)又叫部分總體模式,他建立了對象組的樹形結構,將對象組合成樹狀結構以表示「總體 - 部分」的層次關係。
二、組合模式使得用戶對單個對象和組合對象的訪問具備一致性,即:組合能讓客戶以一致的方式處理個別對象以及組合對象
Component(抽象構件):定義參加組合對象的公有方法和屬性,能夠定義一些默認的行爲和屬性。
Composite(容器構件):樹枝對象,它的做用是組合樹枝結點和葉子結點造成一個樹形結構。
Leaf(葉子構件):葉子構件的下面沒有其餘分支,也就是遍歷的最小單位。
組合模式有兩種實現:安全模式和透明模式,其結構以下圖所示
Composite
類中聲明並實現這些方法。要求:在頁面展現出公司的部門組成(一個公司有多個部門,每一個部門有多個小組);
這是一種很明顯的樹形結構,所以能夠用組合模式解決
「抽象構件」:OrganizationComponent
public abstract class OrganizationComponent { private String name; public OrganizationComponent(String name) { this.name = name; } protected void add(OrganizationComponent component) { throw new UnsupportedOperationException("不支持添加操做"); } protected void remove(OrganizationComponent component) { throw new UnsupportedOperationException("不支持刪除操做"); } protected abstract void print(); public String getName() { return name; } public void setName(String name) { this.name = name; } }
「容器構件」:Company、Department
public class Company extends OrganizationComponent { private List<OrganizationComponent> components = new ArrayList<>(); public Company(String name) { super(name); } @Override protected void add(OrganizationComponent component) { components.add(component); } @Override protected void remove(OrganizationComponent component) { components.remove(component); } @Override protected void print() { System.out.println("======="+getName()+"======="); for (OrganizationComponent component : components) { component.print(); } } @Override public String getName() { return super.getName(); } }
public class Department extends OrganizationComponent { private List<OrganizationComponent> components = new ArrayList<>(); public Department(String name) { super(name); } @Override protected void add(OrganizationComponent component) { components.add(component); } @Override protected void remove(OrganizationComponent component) { components.remove(component); } @Override protected void print() { System.out.println("======="+getName()+"======="); for (OrganizationComponent component : components) { component.print(); } } @Override public String getName() { return super.getName(); } }
「葉子構件」:Group,葉子構件不沒有子節點了,因此不須要添加、刪除之類的方法
public class Group extends OrganizationComponent { public Group(String name) { super(name); } @Override protected void print() { System.out.println(getName()); } @Override public String getName() { return super.getName(); } }
「測試類」:Client
public class Client { @Test public void test01(){ OrganizationComponent company = new Company("阿里巴巴"); OrganizationComponent department1 = new Department("市場部"); OrganizationComponent department2 = new Department("技術部"); OrganizationComponent group1 = new Group("市場一組"); OrganizationComponent group2 = new Group("市場二組"); OrganizationComponent group3 = new Group("技術一組"); OrganizationComponent group4 = new Group("技術二組"); //添加部門 company.add(department1); company.add(department2); //添加小組 department1.add(group1); department1.add(group2); department2.add(group3); department2.add(group4); //打印結果 company.print(); } }
「運行結果」
=======阿里巴巴======= =======市場部======= 市場一組 市場二組 =======技術部======= 技術一組 技術二組
在 Java(jdk 1.8爲例) 的集合類 HashMap 中,抽象構件是 Map,容器構件是 HashMap,葉子構件是 Node
進入源碼能夠看見,在 Map 中定義了許多公共方法
HashMap 實現了 Map,並對一些方法重寫,並且 HashMap 中有一個靜態內部類 Node,它就充當了葉子構件的角色,Node 中去除了 put、putAll 等方法,下面也沒有子結點了
使用:
@Test public void test02(){ Map<String, String> map = new HashMap<>(); map.put("k1", "v1"); map.put("k2", "v2"); System.out.println(map); }
當咱們 put 一個鍵值對的時候,在 HashMap 內部會調用 putVal 方法,將鍵值對封裝爲 Node。
一、簡化客戶端操做。客戶端只須要面對一致的對象而不用考慮總體部分或者節點葉子的問題。
二、具備較強的擴展性。當咱們要更改組合對象時,咱們只須要調整內部的層次關係,客戶端不用作出任何改動。
三、方便建立出複雜的層次結構。客戶端不用理會組合裏面的組成細節,容易添加節點或者葉子從而建立出複雜的樹形結構。
四、須要遍歷組織機構,或者處理的對象具備樹形結構時,很是適合使用組合模式。
五、要求較高的抽象性。若是節點和葉子有不少差別性的話,好比不少方法和屬性都不同,不適合使用組合模式。
裝飾者模式屬於結構型模式,它能夠動態的將新功能附加到對象上,同時又不改變其結構。在對象功能擴展方面,它比繼承更有彈性,裝飾者模式也體現了開閉原則(OCP)。
裝飾者和被裝飾者有相同的超類型,由於裝飾者和被裝飾者必須是同樣的類型,利用繼承是爲了達到類型的匹配,而不是利用繼承獲取行爲
在咖啡店客人想點一杯加兩份糖一份牛奶的摩卡咖啡,各個商品的價格以下,咱們須要根據用戶點的咖啡、加的配料,動態的計算價格
商品 | 價格 |
---|---|
拿鐵咖啡(LatteCoffee) | 4.5 |
摩卡咖啡(MochaCoffe) | 5.5 |
糖(Sugar) | 1.0 |
牛奶(Milk) | 2.0 |
「實體類」 Coffee
public abstract class Coffee{ public String des = "咖啡"; //描述 private float price = 0.0f; //價格 protected abstract float cost(); //計算費用 //省略getter setter方法 }
「被裝飾者」LatteCoffee
public class LatteCoffee extends Coffee{ public LatteCoffee() { setDes("拿鐵咖啡"); setPrice(4.5f); } @Override protected float cost() { return getPrice(); } }
「被裝飾者」MochaCoffee
public class MochaCoffee extends Coffee { public MochaCoffee() { setDes("摩卡咖啡"); setPrice(5.5f); } @Override protected float cost() { return getPrice(); } }
「抽象裝飾者」Decorator
public class Decorator extends Coffee { private Coffee coffee; public Decorator(Coffee drink) { this.coffee = drink; } @Override protected float cost() { return getPrice() + coffee.cost(); } @Override public String getDes() { return coffee.getDes() + "加" + super.getDes(); } }
「具體裝飾者」SugarDecorator
public class SugarDecorator extends Decorator{ public SugarDecorator(Coffee coffee) { super(coffee); setDes("糖"); setPrice(1.0f); } }
「具體裝飾者」MilkDecorator
public class MilkDecorator extends Decorator{ public MilkDecorator(Coffee coffee) { super(coffee); setDes("牛奶"); setPrice(2.0f); } }
「測試類」Client
public class Client { /** * 點一杯 加兩份糖一份牛奶的摩卡咖啡 */ @Test public void test01() { Coffee order = new MochaCoffee(); System.out.println(order.getDes() + ",價格:" + order.cost()); //加兩份糖 order = new SugarDecorator(new SugarDecorator(order)); System.out.println(order.getDes() + ",價格:" + order.cost()); //加一份牛奶 order = new MilkDecorator(order); System.out.println(order.getDes() + ",價格:" + order.cost()); } }
「結果」result
摩卡咖啡,價格:5.5 摩卡咖啡加糖加糖,價格:7.5 摩卡咖啡加糖加糖加牛奶,價格:9.5
在上圖所示的關係中
具體使用以下:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("G:\\a.txt"));
一、利用繼承設計子類,只能在編譯時靜態決定,而且全部子類都會繼承相同的行爲;利用組合擴展對象,就能夠在運行時動態的進行擴展。
二、裝飾者和被裝飾者對象有相同的超類型,因此在任何須要原始對象(被裝飾者)的場合,均可以用裝飾過的對象代替原始對象。
三、能夠用一個或多個裝飾者包裝一個對象(被裝飾者)。
四、裝飾者能夠在所委託的裝飾者行爲以前或以後加上本身的行爲,以達到特定的目的。
五、被裝飾者能夠在任什麼時候候被裝飾,因此能夠在運行時動態地、不限量地用你喜歡的裝飾者來裝飾對象。
六、裝飾者會致使出現不少小對象,若是過分使用,會讓程序變得複雜。
外觀模式(Facade Pattern):外部與一個子系統的通訊必須經過一個統一的外觀對象進行,它爲子系統中的一組接口提供一個統一的高層接口,使子系統更容易被使用。
外觀模式又稱爲門面模式,它是一種對象結構型模式。
一、Client(客戶端):調用者
二、Facade(外觀類):即上述所講的高層接口
三、SubSystem(子系統):被調用者
想要使用電腦,你只須要按一下開機鍵(客戶端),電腦的各個部件(子系統)就開始工做了,你不須要關心硬盤如何啓動的,CPU怎麼運轉的等等,一切都交給內部程序(外觀類)處理。
編寫簡單的程序模擬一下
「SubSystem」:電腦的幾個部件 CPU、內存、硬盤
public class Cpu { //使用「單例模式--餓漢式」建立對象 private static Cpu instance = new Cpu(); private Cpu() { } public static Cpu getInstance() { return instance; } public void start() { System.out.println("CPU啓動"); } public void stop() { System.out.println("CPU中止工做"); } }
public class Memory { private static Memory instance = new Memory(); private Memory() { } public static Memory getInstance() { return instance; } public void start() { System.out.println("內存啓動"); } public void stop() { System.out.println("內存中止工做"); } }
public class HardDisk { private static HardDisk instance = new HardDisk(); private HardDisk() { } public static HardDisk getInstance() { return instance; } public void start() { System.out.println("硬盤啓動"); } public void stop() { System.out.println("硬盤中止工做"); } }
「Facade」:電腦,統一管理開機關機中硬件的啓動與中止
public class Computer { private Cpu cpu; private Memory memory; private HardDisk hardDisk; public Computer() { this.cpu = Cpu.getInstance(); this.memory = Memory.getInstance(); this.hardDisk = HardDisk.getInstance(); } /** * 開機 */ public void boot(){ cpu.start(); memory.start(); hardDisk.start(); } /** * 關機 */ public void shutdown(){ cpu.stop(); memory.stop(); hardDisk.stop(); } }
「Client」:電源鍵,可控制開機、關機
public class Client { Computer computer = new Computer(); @Test public void boot(){ computer.boot(); } @Test public void shutdown(){ computer.shutdown(); } }
優勢:
缺點:
享元模式(Flyweight Pattern)也叫蠅量模式,運用共享技術有效地支持大量細粒度對象的複用。經常使用於系統底層開發,解決系統性能問題。例如數據庫鏈接池,裏面都是建立好的鏈接對象,若是有咱們須要的,直接拿來用,避免從新建立,能夠解決重複對象對內存形成浪費的問題。
享元模式提出了細粒度和共享對象,這裏就涉及了內部狀態和外部狀態的概念,便可以把對象的信息分爲兩個部分:內部狀態和外部狀態
內部狀態(Intrinsic State):能夠共享的相同內容
外部狀態(Extrinsic State):須要外部環境來設置的不能共享的內容
舉個栗子,圍棋理論上有 361 個位置能夠放棋子,每盤棋可能會產生兩三百個棋子對象,因爲內存有限,一臺服務器很難支持更多玩家進行圍棋對戰,若是用享元模式來處理棋子,將棋子的顏色(黑與白)做爲內部狀態,棋子的位置(不肯定)做爲外部狀態,就能夠將棋子對象減小到兩個實例(黑棋、白棋),這樣就能夠很好的解決內存開銷問題。
一個開發團隊接了這樣的項目,客戶但願作一個產品展現網站,但網站須要有多種發佈形式,每一個用戶能夠以新聞形式發佈、以博客形式發佈、以微信公衆號形式發佈...
「抽象享元類」
public abstract class AbstractWebsite { public abstract void publish(User user); }
「非共享具體享元類」
public class User { private String name; public User(String name) { this.name = name; } public String getName() { return name; } }
「具體享元類」
public class ConcreteWebsite extends AbstractWebsite { /** * 發佈類型 */ private String type = ""; public ConcreteWebsite(String type) { this.type = type; } /** * 發佈 */ @Override public void publish(User user) { System.out.println("用戶「"+user.getName()+"」發佈的網站形式爲「" + type+"」"); } }
「享元工廠類」
public class WebsiteFactory { /** * 以 HashMap 做爲對象池 */ private Map<String, ConcreteWebsite> pool = new HashMap<>(); /** * 從對象池中返回指定類型的對象,沒有則建立 */ public AbstractWebsite getWebsite(String type) { if (!pool.containsKey(type)) { pool.put(type, new ConcreteWebsite(type)); } return pool.get(type); } /** * 計算對象池中對象的個數 */ public int count() { return pool.size(); } }
「測試類」
public class Client { @Test public void test(){ WebsiteFactory factory = new WebsiteFactory(); AbstractWebsite website1 = factory.getWebsite("新聞"); website1.publish(new User("張三")); website1.publish(new User("李四")); AbstractWebsite website2 = factory.getWebsite("博客"); website2.publish(new User("王五")); website2.publish(new User("趙六")); AbstractWebsite website3 = factory.getWebsite("公衆號"); website3.publish(new User("陳七")); website3.publish(new User("胡八")); System.out.println("對象的個數:" + factory.count()); } }
「運行結果」
用戶「張三」發佈的網站形式爲「新聞」 用戶「李四」發佈的網站形式爲「新聞」 用戶「王五」發佈的網站形式爲「博客」 用戶「趙六」發佈的網站形式爲「博客」 用戶「陳七」發佈的網站形式爲「公衆號」 用戶「胡八」發佈的網站形式爲「公衆號」 對象的個數:3
首先咱們看一段代碼,運行結果是什麼?
public class IntegerSource { public static void main(String[] args) { Integer v1 = 127; Integer v2 = 127; System.out.println("v1等於v2? " + (v1 == v2)); Integer v3 = 128; Integer v4 = 128; System.out.println("v3等於v4? " + (v3 == v4)); } }
v1等於v2? true v3等於v4? false
分析:查看 Integer 源碼,找到 valueOf
方法,能夠看到,若是 i
在某個範圍內,就不會產生新的對象,直接從緩存數組中獲取,點進 IntegerCache
裏就會發現 low = -128
high = 127
,所以,咱們能夠理解爲這個數組就是「內部狀態」
public static Integer valueOf(int i) { //low = -128 , high = 127 if (i >= IntegerCache.low && i <= IntegerCache.high) //IntegerCache.cache是一個常量數組:static final Integer cache[]; return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
優勢:
能夠極大減小內存中對象的數量,使得相同對象或類似對象在內存中只保存一份。
享元模式的外部狀態相對獨立,並且不會影響其內部狀態,從而使得享元對象能夠在不一樣的環境中被共享。
缺點:
適用場景:
一個系統有大量相同或者類似的對象,因爲這類對象的大量使用,形成內存的大量耗費。
對象的大部分狀態均可之外部化,能夠將這些外部狀態傳入對象中。
使用享元模式須要維護一個存儲享元對象的享元池,而這須要耗費資源,所以,應當在屢次重複使用享元對象時才值得使用享元模式。
代理模式提供了對目標對象額外的訪問方式,即經過代理對象訪問目標對象,這樣能夠在不修改原目標對象的前提下,提供額外的功能操做,擴展目標對象的功能。
代理模式分爲三類:
要求目標對象和代理對象實現同一個接口,調用的時候調用代理對象的方法,從而達到加強的效果
優勢:
能夠在不修改目標對象的前提下,加強目標對象方法的功能(全部代理模式均可以實現,所以不推薦使用此方法)
缺點:
① 冗餘。目標對象和代理對象實現同一個接口,會產生過多的代理類。
② 不易維護。當接口方法增長,目標對象與代理對象都要進行修改。
場景:廠家生產了商品,可是沒有足夠的精力、人力去銷售,這時候就須要一個代理商幫他售賣,可是代理商須要從中抽取 20% 的利潤。
公共接口
public interface IProducer { void sale(float money); }
被代理對象
public class Producer implements IProducer { @Override public void sale(float money) { System.out.println("賣出產品,廠家得到" + money + "元"); } }
代理對象
public class ProxyProducer implements IProducer{ private IProducer producer; public ProxyProducer(IProducer producer) { this.producer = producer; } @Override public void sale(float money) { producer.sale(money * 0.8f); } }
測試類
public class Client { @Test public void test(){ IProducer producer = new Producer(); ProxyProducer proxyProducer = new ProxyProducer(producer); proxyProducer.sale(1000f); } }
運行結果
賣出產品,廠家得到800.0元
動態代理也稱:JDK 代理、接口代理,須要目標對象實現接口,不然不能用動態代理,利用 JDK 的 API(java.lang.reflect.Proxy),動態地在內存中構建代理對象。
靜態代理和動態代理的區別:
以靜態代理的情景爲例,咱們只須要修改代理對象的代碼,代理對象不須要實現公共接口了。
public class ProxyProducer { /** * 維護一個目標對象 */ private Object target; public ProxyProducer(Object target) { this.target = target; } public Object getProxyInstance() { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { /** * 執行被代理對象的任何接口方法都會通過這裏 * @param proxy 代理對象的引用 * @param method 當前執行的方法 * @param args 當前執行方法的參數 * @return 和被代理對象具備相同的返回值 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //代理過程當中執行一些方法 float money = (float) args[0] * 0.8f; //反射機制調用目標對象的方法 Object invoke = method.invoke(target, money); return invoke; } }); } }
Cglib 代理也叫子類代理,目標對象不須要實現任何接口,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展。
Cglib 是一個強大的高性能的代碼生成包,它能夠在運行期間擴展 Java 類與實現 Java 接口,它普遍地被許多 AOP 的框架使用,例如 Spring AOP,用於實現方法攔截。
Cglib 包底層實經過使用字節碼處理框架 ASM 來轉換字節碼並生成新的類。
在 AOP 編程中選擇哪一種代理模式?
使用以前須要導入相關 jar 包,可去 maven 倉庫下載
被代理對象,無需實現接口
public class Producer { public void sale(float money) { System.out.println("賣出產品,廠家得到" + money + "元"); } }
代理對象
public class ProxyProducer implements MethodInterceptor { /** * 維護一個目標對象 */ private Object target; public ProxyProducer(Object target) { this.target = target; } /** * 爲目標對象生成代理對象 */ public Object getProxyInstance(){ //建立一個工具類 Enhancer enhancer = new Enhancer(); //設置父類 enhancer.setSuperclass(target.getClass()); //設置回調函數 enhancer.setCallback(this); //建立子類對象(代理對象) return enhancer.create(); } /** * 會攔截被代理對象的全部方法 * @param obj 加強對象 * @param method 被代理對象的方法 * @param args 被代理對象方法的參數 * @param methodProxy 代理對象 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("obj:" + obj.getClass()); Object returnValue = null; float money = (float) args[0] * 0.8f; if("sale".equals(method.getName())){ returnValue = method.invoke(target, money); } return returnValue; } }
測試類
public class Client { @Test public void test() { Producer producer = new Producer(); Producer proxyInstance = (Producer) new ProxyProducer(producer).getProxyInstance(); proxyInstance.sale(1000f); } }
🎉 以上全部代碼和筆記都可在 個人GitHub 獲取