《Head First 設計模式》學習筆記——代理模式

設計模式
代理模式:爲另一個對象提供一個替身或佔位符以控制對這個對象的訪問。
使用代理模式創建代表對象,讓代表對象控制某對象的訪問,被代理的對象可以使遠程的對象(遠程代理)、創建開銷大的對象(虛擬代理),或需要安全控制的對象(保護代理)。

遠程代理:可以作爲另一個JVM上對象的本地代表。調用代理的方法,會被代理利用網絡轉發到遠程執行,並且結果會通過網絡返回給代理,再由代理將結果返回給客戶。
虛擬代理:作爲創建開銷大的對象的代表。虛擬代理經常直到我們真正需要一個對象的時候才創建它。當對象在創建前和創建中時,由虛擬代理來扮演對象的替身。對象創建後代理就會將請求直接委託給對象。
動態代理:java在java.lang.reflect包中有自己的代理支持,利用這個包你可以在運行時動態的創建一個代理類,實現一個或多個接口,並將方法的調用轉發到你所指定的類。實際的代理類是在運行時創建的,在Java中我們稱這種技術爲動態代理。利用Java的動態代理,可以實現保護代理。
防火牆代理:控制網絡資源的訪問,保護主題免於“壞客戶”的侵害。
智能引用代理:當主題被引用時,進行額外的動作,例如計算一個對象被引用的次數。
緩存代理:爲開銷大的運算結果提供暫時存儲,它也運行多個客戶共享結果,以減少計算或網絡延遲。
同步代理:多線程的情況下爲主題提供安全的訪問。
複雜隱藏代理:用來隱藏一個類的複雜集合的複雜度,並進行訪問控制。有時候也成爲外觀代理。複雜隱藏代理和外觀模式不一樣,因爲代理控制訪問,而外觀模式只是提供另一組接口。
寫入時複製代理:用來控制對象的複製,方法是延遲對象的複製,知道客戶真正需要爲止,這是虛擬代理的變體。

製作遠程服務
(1)製作遠程接口
(2)製作遠程實現
(3)利用rmic產生stub和skeleton
(4)啓動RMI registry
(5)開始遠程服務

設計原則
封裝變化
多用組合,少用繼承
針對接口編程,不針對實現編程
爲交互對象之間的送耦合設計而努力
類應該對擴展開發,對修改關閉
依賴抽象,而不依賴具體類
只和朋友交談
別找我,我會找你
類應該只有一個改變的理由

要點
代理在結構上類似裝飾者,但是目的不一樣。裝飾者模式爲對象加上行爲,而代理則是控制行爲。
和其他包裝者一樣,代理會造成你的設計中類的數目增加。

遠程代理
import java.rmi.Remote;//用來做rmiregistry lookup的naming類在java.rmi包中
import java.rmi.RemoteException;

//extends Remote這表示此接口要用來支持遠程調用
public interface GumballMachineRemote extends Remote {
	//準備支持的方法,每個都要拋出RemoteException
	//因爲每次遠程方法調用都必須考慮成“有風險的”
	public int getCount() throws RemoteException;
	public String getLocation() throws RemoteException;

	//返回值將從服務器經過網絡運回客戶,所以必須是原語類型或可序列化類型
	public State getState() throws RemoteException;
}

//擴展Serializable接口,使得State可序列化
public interface State extends Serializable {
	public void insertQuarter();
	public void ejectQuarter();
	public void turnCrank();
	public void dispense();
}


public class HasQuarterState implements State {
	private static final long serialVersionUID = 768887299984514010L;
	Random randomWinner = new Random(System.currentTimeMillis());
	
	//對於State的每個市縣,我們都在GumballMachine實例變量前面加上transient關鍵字,這樣就可以高考JVM不要序列化這個字段
	transient GumballMachine gumballMachine;

	//其他方法
}

//GumballMachine 要繼承UnicastRemoteObject成爲一個遠程服務
//GumballMachine 也需要實現GumballMachineRemote這個遠程接口
public class GumballMachine extends UnicastRemoteObject implements
		GumballMachineRemote {
	/**
	 * 
	 */
	private static final long serialVersionUID = -2838970117227273571L;
	State soldOutState;
	State noQuarterState;
	State hasQuarterState;
	State soldState;
	State winnerState;
	State state = soldOutState;
	int count = 0;
	String location;

	//構造器需要拋出RemoteException,因爲超類是這樣做的
	public GumballMachine(String location, int numberGumballs)
			throws RemoteException {
		soldOutState = new SoldOutState(this);
		noQuarterState = new NoQuarterState(this);
		hasQuarterState = new HasQuarterState(this);
		soldState = new SoldState(this);
		winnerState = new WinnerState(this);

		this.count = numberGumballs;
		if (numberGumballs > 0)
			state = noQuarterState;
		this.location = location;
	}

	public void insertQuarter() {
		state.insertQuarter();
	}

	public void ejectQuarter() {
		state.ejectQuarter();
	}

	public void turnCrank() {
		state.turnCrank();
		state.dispense();
	}

	void setState(State state) {
		this.state = state;
	}

	void releaseBall() {
		System.out.println("A gumball comes rolling out the slot...");
		if (count != 0) {
			count = count - 1;
		}
	}

	public void refill(int count) {
		this.count = count;
		state = noQuarterState;
	}

	public int getCount() {
		return count;
	}

	public State getState() {
		return state;
	}

	public String getLocation() {
		return location;
	}
	
	//其他方法
}

//在RMI register中註冊
public class GumballMachineTestDrive {

	public static void main(String[] args) {
		GumballMachineRemote gumballMachine = null;
		int count;

		if (args.length < 2) {
			System.out.println("GumballMachine <name> <inventory>");
			System.exit(1);
		}

		try {//需要在實例化代碼前加上try/catch,因爲我們的構造器可能會拋出異常
			count = Integer.parseInt(args[1]);

			gumballMachine = new GumballMachine(args[0], count);
			
			//我們也添加上對Naming.rebind的調用,用gumballmachine的名字發佈GumballMachine的stub
			Naming.rebind("//" + args[0] + "/gumballmachine", gumballMachine);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

//GumballMonitor客戶端
public class GumballMonitor {
	//依賴此GumballMachineRemote遠程接口,而不是具體的類
	GumballMachineRemote machine;

	public GumballMonitor(GumballMachineRemote machine) {
		this.machine = machine;
	}

	public void report() {
		//當我們視圖調用哪些最終都要通過網絡發生的方法時,我麼需要捕獲所有可能發生的遠程異常
		try {
			System.out.println("Gumball Machine: " + machine.getLocation());
			System.out.println("Current inventory: " + machine.getCount()
					+ " gumballs");
			System.out.println("Current state: " + machine.getState());
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}
}

//監視測試程序
public class GumballMonitorTestDrive {

	public static void main(String[] args) {
		String[] location = { "rmi://santafe.mightygumball.com/gumballmachine",
				"rmi://boulder.mightygumball.com/gumballmachine",
				"rmi://seattle.mightygumball.com/gumballmachine" };

		GumballMonitor[] monitor = new GumballMonitor[location.length];

		for (int i = 0; i < location.length; i++) {
			try {
				//爲每個遠程機器創建一個代理,客戶從Register中尋找代理,也就是stub對象
				GumballMachineRemote machine = (GumballMachineRemote) Naming
						.lookup(location[i]);
				monitor[i] = new GumballMonitor(machine);
				System.out.println(monitor[i]);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		//遍歷每臺機器,打印報告
		for (int i = 0; i < monitor.length; i++) {
			monitor[i].report();
		}
	}
}


虛擬代理
class ImageProxy implements Icon {
	ImageIcon imageIcon;
	URL imageURL;
	Thread retrievalThread;
	boolean retrieving = false;
    
	//我們將圖像的url傳入構造器中,這是我們希望顯示的圖像所在的位置
	public ImageProxy(URL url) { imageURL = url; }
     
	public int getIconWidth() {
		if (imageIcon != null) {
            return imageIcon.getIconWidth();
        } else {
			//圖像加載完畢前,返回默認寬和高
			return 800;
		}
	}
 
	public int getIconHeight() {
		if (imageIcon != null) {
            return imageIcon.getIconHeight();
        } else {
			return 600;
		}
	}
     
	public void paintIcon(final Component c, Graphics  g, int x,  int y) {
		if (imageIcon != null) {
			//如果已經有了icon,就告訴它畫出自己
			imageIcon.paintIcon(c, g, x, y);
		} else {
			g.drawString("Loading CD cover, please wait...", x+300, y+190);
			if (!retrieving) {//如果我們還沒試着取出圖像
				retrieving = true;

				//我們不希望整個用戶界面被掛起,所以用另一個線程取出圖像
				retrievalThread = new Thread(new Runnable() {
					public void run() {
						try {
							//此線程中我們實例化icon對象,其構造器會在圖像加載完成後才返回
							imageIcon = new ImageIcon(imageURL, "CD Cover");
							//當圖像準備好後,我們告訴Swing需要重繪
							c.repaint();
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				});
				retrievalThread.start();
			}
		}
	}
}

public class ImageProxyTestDrive {
	ImageComponent imageComponent;
	JFrame frame = new JFrame("CD Cover Viewer");
	JMenuBar menuBar;
	JMenu menu;
	Hashtable<String, String> cds = new Hashtable<String, String>();

	public static void main(String[] args) throws Exception {
		ImageProxyTestDrive testDrive = new ImageProxyTestDrive();
	}

	public ImageProxyTestDrive() throws Exception {
		//建立框架和菜單

		//創建一個圖像代理,並指定初始URL
		Icon icon = new ImageProxy(initialURL);
		//將代理包裝進組件中
		imageComponent = new ImageComponent(icon);
		frame.getContentPane().add(imageComponent);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(800, 600);
		frame.setVisible(true);

	}

	URL getCDUrl(String name) {
		try {
			return new URL((String) cds.get(name));
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return null;
		}
	}
}


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