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