代理模式
第十一个模式:代理模式
11.1问题引入
远程代理的角色:
远程代理就好比“远程对象的本地代表”。所谓“远程对象”?这是一种对象,活在不同的JVM堆中。所谓“本地代表”?这是一种可以由本地方法调用的对象,其行为会转发到远程对象中。
11.2解决方案:
1.制作远程接口:
远程接口定义出可以让客户远程调用的方法。客户用它作为服务的类型。Stub和实际服务都实现此接口。
package net.dp.proxy.gumball; import java.rmi.Remote; import java.rmi.RemoteException; public interface GumballMachineRemote extends Remote { public int getCount() throws RemoteException; public String getLocation() throws RemoteException; public State getState() throws RemoteException; }
我们有一个类型不是可序列化的:Stat类,现在需要修改一下:
package net.dp.proxy.gumball; import java.io.Serializable; public interface State extends Serializable { public void insertQuarter(); public void ejectQuarter(); public void turnCrank(); public void dispense(); }
实际上,我们需要修改状态对象,因为每一个状态对象都维持了一个对糖果机的引用,这样一来,状态对象就可以调用糖果机的方法,改变糖果机的状态。我们不希望整个糖果机都被序列化并随着State对象一起传送弄个。修正这一点:
package net.dp.proxy.gumball; public class NoQuarterState implements State { /** * */ private static final long serialVersionUID = -6945108992684695096L; transient GumballMachine gumballMachine;#对于state的实现,加上transient public NoQuarterState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine; } public void insertQuarter() { System.out.println("You inserted a quarter"); gumballMachine.setState(gumballMachine.getHasQuarterState()); } public void ejectQuarter() { System.out.println("You haven't inserted a quarter"); } public void turnCrank() { System.out.println("You turned, but there's no quarter"); } public void dispense() { System.out.println("You need to pay first"); } public String toString() { return "waiting for quarter"; } }
2.制作远程的实现:
这是做实际工作的类,为远程接口中定义的远程方法提供了真正的实现。这就是客户真正想要调用方法的对象(例如,我们的GumballMachine)。
package net.dp.proxy.gumball; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; 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; 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; } public State getSoldOutState() { return soldOutState; } public State getNoQuarterState() { return noQuarterState; } public State getHasQuarterState() { return hasQuarterState; } public State getSoldState() { return soldState; } public State getWinnerState() { return winnerState; } public String toString() { StringBuffer result = new StringBuffer(); result.append("\nMighty Gumball, Inc."); result.append("\nJava-enabled Standing Gumball Model #2004"); result.append("\nInventory: " + count + " gumball"); if (count != 1) { result.append("s"); } result.append("\n"); result.append("Machine is " + state + "\n"); return result.toString(); } }
糖果机服务已经实现,我们现在现在要将它装上去,好开始接受请求。首先,我们确保将它注册到RMI registry中,好让客户找到她。
现在进行测试:
package net.dp.proxy.gumball; import java.rmi.Naming; 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 { count = Integer.parseInt(args[1]); gumballMachine = new GumballMachine(args[0], count); Naming.rebind("//" + args[0] + "/gumballmachine", gumballMachine); } catch (Exception e) { e.printStackTrace(); } } }
然后,进行测试:
3.利用rmic产生的Stub和skeleton
这是客户和服务的辅助类。你不需要创建这些类。
4.启动RMI registry(rimregistry)
rmireistry就像电话簿,客户可以从中查到代理的位置。
5.开始远程服务
必须启动服务对象。
现在是GumballMonitor客户端:
package net.dp.proxy.gumball; import java.rmi.RemoteException; public class GumballMonitor { 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(); } } }
编写监视器测试程序:
再详细的查看:
11.3引入代理模式:
代理模式:为另一个对象提供一个替身或占位符以控制对这个对象的访问。
11.4介绍虚拟代理:
不同之处:
例子:显示CD封面类图:
工作流程:
编写虚拟ImageProxy:
package net.dp.proxy.virtualproxy; import java.awt.Component; import java.awt.Graphics; import java.net.URL; import javax.swing.Icon; import javax.swing.ImageIcon; class ImageProxy implements Icon { ImageIcon imageIcon; URL imageURL; Thread retrievalThread; boolean retrieving = false; 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) { 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 { imageIcon = new ImageIcon(imageURL, "CD Cover"); c.repaint(); } catch (Exception e) { e.printStackTrace(); } } }); retrievalThread.start(); } } } }
package net.dp.proxy.virtualproxy; import java.awt.Graphics; import javax.swing.Icon; import javax.swing.JComponent; class ImageComponent extends JComponent { /** * */ private static final long serialVersionUID = -4028999850832876573L; private Icon icon; public ImageComponent(Icon icon) { this.icon = icon; } public void setIcon(Icon icon) { this.icon = icon; } public void paintComponent(Graphics g) { super.paintComponent(g); int w = icon.getIconWidth(); int h = icon.getIconHeight(); int x = (800 - w)/2; int y = (600 - h)/2; icon.paintIcon(this, g, x, y); } }
package net.dp.proxy.virtualproxy; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; import java.util.Hashtable; import javax.swing.Icon; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; 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 { @SuppressWarnings("unused") ImageProxyTestDrive testDrive = new ImageProxyTestDrive(); } public ImageProxyTestDrive() throws Exception { cds.put("Ambient: Music for Airports", "http://images.amazon.com/images/P/B000003S2K.01.LZZZZZZZ.jpg"); cds.put("Buddha Bar", "http://images.amazon.com/images/P/B00009XBYK.01.LZZZZZZZ.jpg"); cds.put("Ima", "http://images.amazon.com/images/P/B000005IRM.01.LZZZZZZZ.jpg"); cds.put("Karma", "http://images.amazon.com/images/P/B000005DCB.01.LZZZZZZZ.gif"); cds.put("MCMXC A.D.", "http://images.amazon.com/images/P/B000002URV.01.LZZZZZZZ.jpg"); cds.put("Northern Exposure", "http://images.amazon.com/images/P/B000003SFN.01.LZZZZZZZ.jpg"); cds.put("Selected Ambient Works, Vol. 2", "http://images.amazon.com/images/P/B000002MNZ.01.LZZZZZZZ.jpg"); URL initialURL = new URL((String) cds .get("Selected Ambient Works, Vol. 2")); menuBar = new JMenuBar(); menu = new JMenu("Favorite CDs"); menuBar.add(menu); frame.setJMenuBar(menuBar); for (Enumeration<String> e = cds.keys(); e.hasMoreElements();) { String name = (String) e.nextElement(); JMenuItem menuItem = new JMenuItem(name); menu.add(menuItem); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { imageComponent.setIcon(new ImageProxy(getCDUrl(event .getActionCommand()))); frame.repaint(); } }); } // set up frame and menus 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; } } }
11.5小结: