設計模式:繼承->裝飾設計模式->動態代理

引入

最近學習javaweb視頻時,學習到了一種新的設計模式叫動態代理,對於這種設計模式我在這裏做一下簡單的總結,以免以後忘記了。

在說動態代理的時候我們需要先說一說,裝飾設計模式,裝飾設計模式具有增強功能的作用,下面我先簡單的舉個例子來說說什麼時裝飾設計模式。

首先,我們先假設一個場景,以後有了無人駕駛(雖然這個東西好像現在就有了),這個時候,我們需要用java來開發這個項目,於是呢?sun公司就提供了一種接口叫做car,然後其他大公司就根據這個接口來創建自己的類,但是呢?比如說,谷歌把這個無人駕駛的汽車造出來了,叫做GoogleCar,並且對外開放出售方式,但不提供源碼。某一天呢?有一家公司買了谷歌公司的這個產品,需要對某些功能進行增強,但是呢,他們又不知道這個東西的源碼是怎麼實現的,那麼我們該如何去處理呢?

//sun公司提供的對外接口
public interface Car {

	public void start();
	
	public void run();
	
	public void stop();
}

//谷歌實現的接口
public class GoogleCar implements Car {

	@Override
	public void start() {
		System.out.println("無人汽車準備啓動");
	}

	@Override
	public void run() {
		System.out.println("無人汽車正常運行");
	}

	@Override
	public void stop() {
		System.out.println("無人汽車停止運行");
	}

}

首先,我們先想到的肯定是 繼承 這種方式,因爲這種方式是處理當前狀況最爲簡單的一種處理方式了。

public class MyCar extends GoogleCar{

	@Override
	public void start() {
		//需要增強的功能
		System.out.println("天氣是否良好?");
		System.out.println("路況是否擁堵?");
		super.start();
	}

	@Override
	public void run() {
		super.run();
	}

	@Override
	public void stop() {
		super.stop();
	}
}

以上便是用繼承的方式去處理這個項目,我們大致一看感覺沒什麼問題,可以說是完美解決了這個問題,但是這樣做卻存在一個安全隱患,相關例子如下:

public class One {
	public void  test01() {
		System.out.println("One:test01");
		test02();
	}
	
	public void test02() {
		System.out.println("One:test02");
	}
}

public class Two extends One{

	@Override
	public void test01() {
		super.test01();
	}
	
	@Override
	public void test02() {
//		super.test02();
	}
	
	public static void main(String[] args) {
		Two two = new Two();
		two.test01();
	}
}

以上有兩個類:一個是One,另一個是Two繼承One而來,我說的安全隱患就是,如果我們不小心註釋了一個// super.test02(); 那麼不就會產生這種安全隱患了嗎?所以這種有隱患的情況產生,所以Google肯定會將其GoogleCar進行小小改變如下:
(加上final,禁止繼承,所以我們只能使用裝飾設計模式)

final public class GoogleCar implements Car {
	@Override
	public void start() {
		System.out.println("無人汽車準備啓動");
	}
	@Override
	public void run() {
		System.out.println("無人汽車正常運行");
	}
	@Override
	public void stop() {
		System.out.println("無人汽車停止運行");
	}
}

所以這個時候我們只能使用裝飾設計模式:

public class MyCar implements Car {
	private Car car;
	
	public MyCar(Car car) {
		this.car = car;
	}
	
	@Override
	public void start() {
		System.out.println("天氣是否良好?");
		System.out.println("路況是否擁堵?");
		car.start();
	}

	@Override
	public void run() {
		car.run();
	}

	@Override
	public void stop() {
		car.stop();
	}
}

裝飾設計模式比繼承好在哪?

首先,我們先設想這樣一個場景,有一家飲品店,這家店呢,目前只賣,咖啡,奶茶,啤酒,這三中商品。但是呢他們都是飲料,他們都有一些共同的屬性,就是,所以就有如下代碼:

public interface Drinks {
	public void drink();
}

public class Coffee implements Drinks{
	@Override
	public void drink() {
		System.out.println("喝咖啡");
	}
}

public class TeaWithMilk implements Drinks {
	@Override
	public void drink() {
		System.out.println("喝奶茶");
	}
}

public class Beer implements Drinks {
	@Override
	public void drink() {
		System.out.println("喝啤酒");
	}
}

然後呢?某一天呢?有人想喝加糖的咖啡,於是呢?我就在創建一個類來繼承這個Coffee這個類,又有人想喝加糖的奶茶,這個時候又加一個類,再後來呢?居然有人想喝加糖的啤酒,所以在加上一個類,就這樣如此下去,那麼我們這個體系將會越來越臃腫,顯得特別不夠靈活。

所以這個時候我們使用裝飾設計模式來處理這個問題,但是這個時候我們又產生了一個問題,我們就不能直接創建一個方法來處理這個問題嗎?也可以我們創建一個方法就行呢,來了什麼就加糖,但是這樣會又一個問題,什麼問題呢?在這裏我們的Drinks這個類只有一個drink()方法,但如果我們有很多個,每個方法都需要增強,是不是又要創建很多個方法來處理呢,那爲什麼,就不把這些方法封裝到一個類裏面呢,一起處理了,這個類不就變成了裝飾設計模式嗎?

裝飾模式比繼承要靈活。避免了繼承體系臃腫。
而且降低了類於類之間的關係。

走心的豬:淺析面向對象之——裝飾者模式之於繼承機制的存在價值與意義

裝飾設計模式的弊端—>動態代理

當一個接口裏面的方法有200多個方發時,我們只對一些方法進行加強,那麼我們如果還是使用裝飾設計模式的化,那麼會把人搞死的。所以我們有了,動態代理。

我們是用那個谷歌汽車那個例子進行說明.
(在這裏呢,我只能寫寫動態代理的代碼實現,是如何實現的)

public interface ICar {

	public String start(int a,int b) ;
	
	public void run();
	
	public void stop();

}

public class GoogleCar implements ICar {

	@Override
	public String start(int a,int b) {
		System.out.println("谷歌汽車開始啓動");
		return "start___"+a+"___"+b;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("谷歌汽車以在路上運行");
	}

	@Override
	public void stop() {
		// TODO Auto-generated method stub
		System.out.println("谷歌汽車運行結束");
	}
}

public class Test {
	public static void main(String[] args) {
		
		
		//1parm:固定值:告訴虛擬機用哪個字節碼加載器加載內存中創建出的字節碼文件
		//2parm:告訴虛擬機內存中正在被創建的字節碼文件中應該有哪些方法
		//3parm:告訴虛擬機正在被創建的字節碼上的各種方法如何處理
		ICar car = (ICar)Proxy.newProxyInstance(Test.class.getClassLoader(), GoogleCar.class.getInterfaces(), new InvocationHandler() {
			
			//method:代表正在執行的方法
			//args:代表正在執行的方法中的參數
			//Object:代表方法執行完畢之後的返回值
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				Object obj = null;
				if(method.getName().equals("start")) {
					System.out.println("檢查天氣是否良好");	
					//這個方法是反射機制裏面的method.invoke(new GoogleCar(),args);
					obj = method.invoke(new GoogleCar(),args);
					System.out.println("檢查路況是否擁擠");	
				}else {
					obj = method.invoke(new GoogleCar(),args);
				}
				return obj;
			}
		});

		car.start(1,4);
		car.run();
		car.stop();
	}
}

最後呢,來說說動態代理的大致過程,
這個是我的class文件
在這裏插入圖片描述
在這裏插入圖片描述
在這裏呢?我們都知道一個道理,一個class文件就代表一個.java文件,然而這裏卻多了一個東西,

Test$1.class,其實我想說的是這個東西就是動態代理創建出來的。

好了我來說說,動態代理的大致實現過程,首先呢,我們需要一個類加載器,來加載我們的字節碼文件,在

這裏呢,我們的類加載器是Test.class.getClassLoader(),然後呢我們就加載了GoogleCar.class這個文件,然

後這個文件就進入了內存當中,然後呢我們用

GoogleCar.class.getInterfaces(),就知道了這個類中實現的接口上有多少個方法,然後便是我們的 new

InvocationHandler(){}這個便是動態代理的關鍵部分,在裏面呢?有這樣一段代碼:

	if(method.getName().equals("start")) {
			System.out.println("檢查天氣是否良好");	
			obj = method.invoke(new GoogleCar(),args);
			System.out.println("檢查路況是否擁擠");	
	}else {
			obj = method.invoke(new GoogleCar(),args);
	}

在這裏插入圖片描述
這個是我用notepad打開GoogleCar.class的內容,這個對於我們來說是看不懂的,但是對於計算機而言它卻

非常熟悉這個東西,就開始判斷,如果發現方法是start就把他加強後,然後寫入到Test$1.class這個文件中,

這個便是動態代理自動生成的一個文件,如果不是start方法,那就跟簡單了,直接把那個方法編譯好的字節

碼,直接寫入到Test$1.class這個文件中去,等處理完了之後,就直接調用就可以。

以下便是Test$1.class文件的部分截圖

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章