设计模式(2)_代理模式 ————— 控制对象访问
来源:互联网 发布:电脑网络配置怎么设置 编辑:程序博客网 时间:2024/06/03 21:11
设计模式(2)_代理模式 ————— 控制对象访问
一、动机 需求
现在有这样一个需求:有一个出版社,该出版社有一个工厂,专门用来生产制造图书,该工厂里有很多台生产制造图书的机器。每个机器有自己的位置坐标,用 int表示,机器的状态,{正在工作,暂停,故障},已经印刷了多少页图书。在出版社 在工厂 厂长的电脑屏幕上,可以随时打印出任何一台机器的报告信息(report infomation)。
下来 我们用代码实现这个需求:
PrinterMachine.java
package com.crg;public class PrinterMachine { //打印机位置,及第几台打印机 private int location; //打印机当前的打印页数 private int pages; //打印机当前的状态 private int state; public PrinterMachine(int location, int state, int pages){ this.pages = pages; this.location = location; this.state = state; } public int getLocation(){ return location; } public int getPages(){ return pages; } public int getState(){ return state; }}
打印机类,可以获得该打印机的位置信息,当前页数,当前状态;
---------------------------------------------
PrinterMonitor.javapackage com.crg;import com.crg.PrinterMachine;public class PrinterMonitor{ private PrinterMachine printerMachine; public PrinterMonitor(PrinterMachine printerMachine ){ this.printerMachine = printerMachine; } public void report(){ System.out.println("the PrinterMachine location is :" + printerMachine.getLocation()); System.out.println("the PrinterMachine state is :" + printerMachine.getState()); System.out.println("the PrinterMachine pages is :" + printerMachine.getPages()); }}
打印机监控类,可以打印出 那个位置的打印机的当前信息,并生成报告
---------------------------------------------
PrinterMonitorTest.javapackage com.crg;import com.crg.PrinterMachine;import com.crg.PrinterMonitor;public class PrinterMonitorTest{ public static void main(String args[]){ if(args.length < 3){ System.out.println("the args must be three or more"); System.exit(1); } int location = Integer.parseInt(args[0]); int state = Integer.parseInt(args[1]); int pages = Integer.parseInt(args[2]); PrinterMachine printerMachine = new PrinterMachine(location, state, pages); PrinterMonitor printerMonitor = new PrinterMonitor(printerMachine); printerMonitor.report(); }}
---------------------------------------------
测试类,从命令行窗口,接受三个参数,分别为 打印机位置,打印机当前状态,当前打印页数
运行效果如下:
crg@crg-pc:~/my_learning/code/proxypattern$ javac com/crg/PrinterMonitorTest.javacrg@crg-pc:~/my_learning/code/proxypattern$ java com/crg/PrinterMonitorTest 12 1 1126the PrinterMachine location is :12the PrinterMachine state is :1the PrinterMachine pages is :1126
二、新的需求
一中的实现是在,同一台电脑,同一个JVM 上运行的,现在 又有一个新的需求,打印机PrinterMachine.java 运行在工厂的 JVM 上,而厂长要在 自己的办公室里,查看打印机监察器(PrinterMonitor.java)的 打印报告,即 打印机监察器运行在,厂长办公室的JVM上。但是呢,要想获得,打印机的详细信息报告,就必须把 PrinterMachine.java 对象传给 PrinterMonitor.java,打印机监察器才能生成报告。
解决办法如下:
不需要修改之前的代码,把远程PrinterMachine.java 在本地的代理对象 交给打印机监察器,把这个打印机在本地的代理对象(proxy),当做真正的 PrinterMachine.java 对象,其实一切的动作,是,代理对象利用网络和真正的打印机对象沟通.
三、利用java 内置 JAVA RMI 实现远程访问
1、制作远程接口
package com.crg;import java.rmi.Remote;import java.rmi.RemoteException;public interface MyRemote extends Remote { public String sayHello() throws RemoteException;}
2、制作远程服务实现
package com.crg;import java.rmi.Naming;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote { /** * */ private static final long serialVersionUID = 1L; protected MyRemoteImpl() throws RemoteException { super(); } @Override public String sayHello() throws RemoteException { return "Server says 'Hey'"; } public static void amin(String[] args) { try { MyRemote service = new MyRemoteImpl(); Naming.rebind("RemoteHello", service); } catch (Exception e) { e.printStackTrace(); } }}
3、产生 Stub(客户辅助对象,也就是远程服务对象的代理),Skeleton(服务辅助对象)
使用如下命令即可:
-rw-rw-r-- 1 crg crg 600 7月 17 17:25 MyRemoteImpl.java-rw-rw-r-- 1 crg crg 153 7月 17 17:25 MyRemote.javacrg@crg-pc:~/my_learning/code/com/crg$ javac MyRemoteImpl.javacrg@crg-pc:~/my_learning/code/com/crg$ rmic -keep -v1.1 MyRemoteImplcrg@crg-pc:~/my_learning/code/com/crg$ lltotal 40drwxrwxr-x 2 crg crg 4096 7月 17 17:31 ./drwxrwxr-x 3 crg crg 4096 7月 17 16:16 ../-rw-rw-r-- 1 crg crg 215 7月 17 17:31 MyRemote.class-rw-rw-r-- 1 crg crg 771 7月 17 17:31 MyRemoteImpl.class-rw-rw-r-- 1 crg crg 600 7月 17 17:25 MyRemoteImpl.java-rw-rw-r-- 1 crg crg 1362 7月 17 17:31 MyRemoteImpl_Skel.class-rw-rw-r-- 1 crg crg 1260 7月 17 17:31 MyRemoteImpl_Skel.java-rw-rw-r-- 1 crg crg 1604 7月 17 17:31 MyRemoteImpl_Stub.class-rw-rw-r-- 1 crg crg 1566 7月 17 17:31 MyRemoteImpl_Stub.java-rw-rw-r-- 1 crg crg 153 7月 17 17:25 MyRemote.java
4、实现客户端
import java.net.MalformedURLException;import java.rmi.Naming;import java.rmi.NotBoundException;import java.rmi.RemoteException;public class MyRemoteClient { public static void main(String[] args) { new MyRemoteClient().go(); } public void go(){ MyRemote service = null; try { service = (MyRemote) Naming.lookup("rmi://10.0.0.54/RemoteHello"); } catch (MalformedURLException e1) { e1.printStackTrace(); } catch (RemoteException e1) { e1.printStackTrace(); } catch (NotBoundException e1) { e1.printStackTrace(); } String result = null; try { result = service.sayHello(); } catch (RemoteException e) { e.printStackTrace(); } System.out.println("the result is: " + result); }}
5、开启一个终端 执行 rmiregistry
6、启动远程服务
crg@crg-pc:~/my_learning/code/com/crg$ java MyRemoteImpl
7、启动客户端
结果如下:
crg@crg-pc:~/Desktop/client$ java MyRemoteClientthe result is: Server says 'Hey'crg@crg-pc:~/Desktop/client$
已经成功实现了 客户端远程调用 服务器端的 方法了
贴出rmic 自动生成的 MyRemoteImpl_Stub.java 和 MyRemoteImpl_Skel.java 的代码
// Stub class generated by rmic, do not edit.// Contents subject to change without notice.public final class MyRemoteImpl_Stub extends java.rmi.server.RemoteStub implements MyRemote, java.rmi.Remote{ private static final java.rmi.server.Operation[] operations = { new java.rmi.server.Operation("java.lang.String sayHello()") }; private static final long interfaceHash = 6486744599627128933L; // constructors public MyRemoteImpl_Stub() { super(); } public MyRemoteImpl_Stub(java.rmi.server.RemoteRef ref) { super(ref); } // methods from remote interfaces // implementation of sayHello() public java.lang.String sayHello() throws java.rmi.RemoteException { try { java.rmi.server.RemoteCall call = ref.newCall((java.rmi.server.RemoteObject) this, operations, 0, interfaceHash); ref.invoke(call); java.lang.String $result; try { java.io.ObjectInput in = call.getInputStream(); $result = (java.lang.String) in.readObject(); } catch (java.io.IOException e) { throw new java.rmi.UnmarshalException("error unmarshalling return", e); } catch (java.lang.ClassNotFoundException e) { throw new java.rmi.UnmarshalException("error unmarshalling return", e); } finally { ref.done(call); } return $result; } catch (java.lang.RuntimeException e) { throw e; } catch (java.rmi.RemoteException e) { throw e; } catch (java.lang.Exception e) { throw new java.rmi.UnexpectedException("undeclared checked exception", e); } }}
// Skeleton class generated by rmic, do not edit.// Contents subject to change without notice.public final class MyRemoteImpl_Skel implements java.rmi.server.Skeleton{ private static final java.rmi.server.Operation[] operations = { new java.rmi.server.Operation("java.lang.String sayHello()") }; private static final long interfaceHash = 6486744599627128933L; public java.rmi.server.Operation[] getOperations() { return (java.rmi.server.Operation[]) operations.clone(); } public void dispatch(java.rmi.Remote obj, java.rmi.server.RemoteCall call, int opnum, long hash) throws java.lang.Exception { if (hash != interfaceHash) throw new java.rmi.server.SkeletonMismatchException("interface hash mismatch"); MyRemoteImpl server = (MyRemoteImpl) obj; switch (opnum) { case 0: // sayHello() { call.releaseInputStream(); java.lang.String $result = server.sayHello(); try { java.io.ObjectOutput out = call.getResultStream(true); out.writeObject($result); } catch (java.io.IOException e) { throw new java.rmi.MarshalException("error marshalling return", e); } break; } default: throw new java.rmi.UnmarshalException("invalid method number"); } }}
四、用远程代理 来改写开始那个图书工厂的例子;
源码地址:
https://github.com/aloe-all/proxy_design_pattern
先看一个逻辑思路图:
如上图所示,我们先实现,远程服务端的 打印机对象 PrinterMachine
package com.crg;import java.rmi.Remote;import java.rmi.RemoteException;/** * 远程接口 * @author crg * 方法的返回类型都必须是 可序列化的,因为要在网络传输 * */public interface PrinterMachineRemote extends Remote { public int getCount() throws RemoteException; public int getLocation() throws RemoteException; public State getState() throws RemoteException;}
package com.crg;import java.io.Serializable;/** * 打印机的状态接口,把此接口序列化,所有的实现类中的 State 就可以在网络上传送了 * @author crg * */public interface State extends Serializable { public int getStateInfo();}
package com.crg;/** * * State 的一个实现类,正常状态 * @author crg * */public class NormalState implements State { /** * PrinterMachineRemote 对象的引用,可以调用 PrinterMachine 的方法; * transient 修饰 PrinterMachine,告诉 JVM 不要实例化该字段 */ private transient PrinterMachineRemote mPrinterMachineRemote; @Override public int getStateInfo() { //0 代表正常状态 return 0; }}
package com.crg;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;public class PrinterMachine extends UnicastRemoteObject implements PrinterMachineRemote { //打印机位置,及第几台打印机 private int location; //打印机当前的打印页数 private int pages; //打印机当前的状态 private State state; protected PrinterMachine(int location, State state, int pages) throws RemoteException { super(); this.pages = pages; this.location = location; this.state = state; } @Override public int getCount() throws RemoteException { // TODO Auto-generated method stub return pages; } @Override public int getLocation() throws RemoteException { // TODO Auto-generated method stub return location; } @Override public State getState() throws RemoteException { // TODO Auto-generated method stub return state; }}
打印机服务已经完成了。现在我们需要将它开启,好开始接受请求,首先我们要确保将他注册到 RMI registry中,好让客户端可以找到它。
服务端代码:
import java.rmi.Naming;public class PrinterMachinePort{ public static void main(String args[]){ PrinterMachine currentMachine; int currentPages; if(args.length < 2){ System.out.println("the args must be two or more"); System.exit(1); } int currentMachineLocation = Integer.parseInt(args[0]); currentPages = Integer.parseInt(args[1]); try { currentMachine = new PrinterMachine(currentMachineLocation, currentPages); Naming.rebind(currentMachineLocation + "_PrinterMachine", currentMachine); } catch (Exception e) { e.printStackTrace(); } }}
本地客户端代码:
package com.crg;import java.rmi.RemoteException;/** * 本地客户端监控器,运行在厂长办公室的jvm上,监控工厂的打印机 * * @author crg * */public class PrinterMonitor{ private PrinterMachineRemote printerMachineRemote; public PrinterMonitor(PrinterMachineRemote printerMachineRemote ){ this.printerMachineRemote = printerMachineRemote; } public void report(){ try { System.out.println("the PrinterMachine state is :" + printerMachineRemote.getState().getStateInfo()); System.out.println("the PrinterMachine location is :" + printerMachineRemote.getLocation()); System.out.println("the PrinterMachine pages is :" + printerMachineRemote.getCount()); } catch (RemoteException e) { e.printStackTrace(); } }}
客户端测试程序代码:
import java.net.MalformedURLException;import java.rmi.Naming;import java.rmi.NotBoundException;import java.rmi.RemoteException;/** * 监视器测试程序 * 厂长会执行这个程序,就可以得到 ,打印机报告 * @author crg * */public class PrinterMachineClientTest { public static void main(String[] args) { //远程打印机 名称数组 String[] location = {"rmi://10.0.0.54/10_PrinterMachine", "rmi://10.0.0.54/20_PrinterMachine", "rmi://10.0.0.54/30_PrinterMachine"}; PrinterMonitor[] printerMonitors = new PrinterMonitor[location.length]; for (int i = 0; i < location.length; i++) { try { //获得远程打印机对象,实际上得到的是远程呢个 打印机的代理 stub PrinterMachineRemote printerMachineRemote = (PrinterMachineRemote) Naming.lookup(location[i]); printerMonitors[i] = new PrinterMonitor(printerMachineRemote); } catch (Exception e) { e.printStackTrace(); } } for (int i = 0; i < printerMonitors.length; i++) { //打印报告 printerMonitors[i].report(); } }}
编译源码
crg@crg-pc:~/workspace/RemotePrinterMachine/src/com/crg$ javac PrinterMachinePort.java
利用 java rmic 生成 代理对象
crg@crg-pc:~/workspace/RemotePrinterMachine/src/com/crg$ rmic -keep -v1.1 PrinterMachinecrg@crg-pc:~/workspace/RemotePrinterMachine/src/com/crg$ lltotal 72drwxrwxr-x 2 crg crg 4096 7月 23 16:52 ./drwxrwxr-x 3 crg crg 4096 7月 21 21:33 ../-rw-rw-r-- 1 crg crg 327 7月 23 16:50 NormalState.class-rw-rw-r-- 1 crg crg 437 7月 23 16:11 NormalState.java-rw-rw-r-- 1 crg crg 677 7月 23 16:50 PrinterMachine.class-rw-rw-r-- 1 crg crg 1089 7月 23 16:44 PrinterMachineClientTest.java-rw-rw-r-- 1 crg crg 908 7月 23 16:49 PrinterMachine.java-rw-rw-r-- 1 crg crg 1110 7月 23 16:51 PrinterMachinePort.class-rw-rw-r-- 1 crg crg 745 7月 23 16:40 PrinterMachinePort.java-rw-rw-r-- 1 crg crg 295 7月 23 16:50 PrinterMachineRemote.class-rw-rw-r-- 1 crg crg 383 7月 23 16:11 PrinterMachineRemote.java-rw-rw-r-- 1 crg crg 1634 7月 23 16:52 PrinterMachine_Skel.class-rw-rw-r-- 1 crg crg 2006 7月 23 16:52 PrinterMachine_Skel.java-rw-rw-r-- 1 crg crg 2092 7月 23 16:52 PrinterMachine_Stub.class-rw-rw-r-- 1 crg crg 3190 7月 23 16:52 PrinterMachine_Stub.java-rw-rw-r-- 1 crg crg 821 7月 23 16:11 PrinterMonitor.java-rw-rw-r-- 1 crg crg 148 7月 23 16:50 State.class-rw-rw-r-- 1 crg crg 249 7月 23 16:11 State.java
启动 rmiregistry 服务
crg@crg-pc:~/workspace/RemotePrinterMachine/src/com/crg$ rmiregistry
新开命令行窗口,启动远程打印机,启动三个不同的 打印机 服务:
crg@crg-pc:~/workspace/RemotePrinterMachine/src/com/crg$ java PrinterMachinePort 10 100crg@crg-pc:~/workspace/RemotePrinterMachine/src/com/crg$ java PrinterMachinePort 20 2000crg@crg-pc:~/workspace/RemotePrinterMachine/src/com/crg$ java PrinterMachinePort 30 300
然后 我在另一台 笔记本上,模拟本地客户端,笔记本上运行的是另一个 jvm 请求的是 台式机的 PrinterMachine 服务
结果如下:
本地客户端,也就是厂长的 笔记本上拿到了 打印机工厂的 打印机的 信息,远程 通过网络调用成功。
事实上 PrinterMachineClientTest.java 调用的是 PrinterMachine 在本地的 代理 PrinterMachine_Stub 来完成远程交互的。
通过调用代理的方法,远程调用可以跨过网络,返回字符串、数组、和State对象。因为我们使用的是代理,调用那方法会在远程执行。
java rmi 生成的代理文件 如下:
// Stub class generated by rmic, do not edit.// Contents subject to change without notice.public final class PrinterMachine_Stub extends java.rmi.server.RemoteStub implements PrinterMachineRemote, java.rmi.Remote{ private static final java.rmi.server.Operation[] operations = { new java.rmi.server.Operation("int getCount()"), new java.rmi.server.Operation("int getLocation()"), new java.rmi.server.Operation("State getState()") }; private static final long interfaceHash = -5260438444927807617L; // constructors public PrinterMachine_Stub() { super(); } public PrinterMachine_Stub(java.rmi.server.RemoteRef ref) { super(ref); } // methods from remote interfaces // implementation of getCount() public int getCount() throws java.rmi.RemoteException { try { java.rmi.server.RemoteCall call = ref.newCall((java.rmi.server.RemoteObject) this, operations, 0, interfaceHash); ref.invoke(call); int $result; try { java.io.ObjectInput in = call.getInputStream(); $result = in.readInt(); } catch (java.io.IOException e) { throw new java.rmi.UnmarshalException("error unmarshalling return", e); } finally { ref.done(call); } return $result; } catch (java.lang.RuntimeException e) { throw e; } catch (java.rmi.RemoteException e) { throw e; } catch (java.lang.Exception e) { throw new java.rmi.UnexpectedException("undeclared checked exception", e); } } // implementation of getLocation() public int getLocation() throws java.rmi.RemoteException { try { java.rmi.server.RemoteCall call = ref.newCall((java.rmi.server.RemoteObject) this, operations, 1, interfaceHash); ref.invoke(call); int $result; try { java.io.ObjectInput in = call.getInputStream(); $result = in.readInt(); } catch (java.io.IOException e) { throw new java.rmi.UnmarshalException("error unmarshalling return", e); } finally { ref.done(call); } return $result; } catch (java.lang.RuntimeException e) { throw e; } catch (java.rmi.RemoteException e) { throw e; } catch (java.lang.Exception e) { throw new java.rmi.UnexpectedException("undeclared checked exception", e); } } // implementation of getState() public State getState() throws java.rmi.RemoteException { try { java.rmi.server.RemoteCall call = ref.newCall((java.rmi.server.RemoteObject) this, operations, 2, interfaceHash); ref.invoke(call); State $result; try { java.io.ObjectInput in = call.getInputStream(); $result = (State) in.readObject(); } catch (java.io.IOException e) { throw new java.rmi.UnmarshalException("error unmarshalling return", e); } catch (java.lang.ClassNotFoundException e) { throw new java.rmi.UnmarshalException("error unmarshalling return", e); } finally { ref.done(call); } return $result; } catch (java.lang.RuntimeException e) { throw e; } catch (java.rmi.RemoteException e) { throw e; } catch (java.lang.Exception e) { throw new java.rmi.UnexpectedException("undeclared checked exception", e); } }}
// Skeleton class generated by rmic, do not edit.// Contents subject to change without notice.public final class PrinterMachine_Skel implements java.rmi.server.Skeleton{ private static final java.rmi.server.Operation[] operations = { new java.rmi.server.Operation("int getCount()"), new java.rmi.server.Operation("int getLocation()"), new java.rmi.server.Operation("State getState()") }; private static final long interfaceHash = -5260438444927807617L; public java.rmi.server.Operation[] getOperations() { return (java.rmi.server.Operation[]) operations.clone(); } public void dispatch(java.rmi.Remote obj, java.rmi.server.RemoteCall call, int opnum, long hash) throws java.lang.Exception { if (hash != interfaceHash) throw new java.rmi.server.SkeletonMismatchException("interface hash mismatch"); PrinterMachine server = (PrinterMachine) obj; switch (opnum) { case 0: // getCount() { call.releaseInputStream(); int $result = server.getCount(); try { java.io.ObjectOutput out = call.getResultStream(true); out.writeInt($result); } catch (java.io.IOException e) { throw new java.rmi.MarshalException("error marshalling return", e); } break; } case 1: // getLocation() { call.releaseInputStream(); int $result = server.getLocation(); try { java.io.ObjectOutput out = call.getResultStream(true); out.writeInt($result); } catch (java.io.IOException e) { throw new java.rmi.MarshalException("error marshalling return", e); } break; } case 2: // getState() { call.releaseInputStream(); State $result = server.getState(); try { java.io.ObjectOutput out = call.getResultStream(true); out.writeObject($result); } catch (java.io.IOException e) { throw new java.rmi.MarshalException("error marshalling return", e); } break; } default: throw new java.rmi.UnmarshalException("invalid method number"); } }}
五、定义代理模式
从上面的例子可以看出,远程代理是一般代理模式的一种体现。
代理模式 :为一个对象提供一个替身或占位符以控制对这个对象的访问。
使用代理模式创建代表(representative)对象,让代表对象控制某对象的访问,被代理的对象可以是远程的对象、创建开销大的对象或需要安全控制的对象。
代理模式 uml类图
首先是 Subject,为 RealSubject 和 Proxy 提供了接口。通过实现同一接口,Proxy在RealSubject出现的地方取代它。
RealSubject 是真正做事的对象,它是被 Proxy代理控制访问的对象
Proxy 持有 RealSubject 的引用。在某些例子中,Proxy 还会负责RealSubject对象的创建和销毁。客户和 RealSubject 之间的交互必须通过 Proxy.因为代理和真实的对象 实现了相同的 接口,所以用到真实对象的地方,都可以用的代理取代。Proxy 控制 RealSubject 对象的访问。RealSubject 可以是远程对象,创建开销大,RealSubject 需要被保护。
上面的两个例子,都是远程代理,再加上 代理模式的 概念,就很容易理解了。下面看看 远程代理和 虚拟代理的比较:
1、远程代理,可以作为另一个JVM上对象的本地代表。调用代理地方法,会被代理利用网络转发到远程执行,并且结果会通过网络返回给代理,再由代理将结果转给客户;
2、虚拟代理,作为创建开销大的对象代表。虚拟代理经常等到我们真正需要创建一个对象的时候才创建它。当对象在创建前和创建中时,由虚拟代理来扮演对象的替身。对象创建后,代理就会将请求直接委托给对象。
- 设计模式(2)_代理模式 ————— 控制对象访问
- 代理模式(Proxy Pattern)——控制对象访问
- 设计模式—代理模式
- 设计模式—代理模式
- 设计模式—代理模式
- 设计模式—代理模式
- 设计模式—代理模式
- 设计模式—代理模式
- 设计模式—代理模式
- 设计模式—代理模式
- 设计模式—代理模式
- 设计模式—代理模式
- 设计模式——代理设计模式
- 设计模式——代理设计模式
- 控制对象访问--代理模式
- PHP设计模式——数据访问对象模式
- 设计模式——访问设计模式
- 每日设计模式——代理模式
- 排序-归并排序
- 如何在MyEclipse中使用MyBatis将MySQL数据表生成相应的实体类和映射文件
- java设计模式
- Java - Java books
- Java中的数据类型
- 设计模式(2)_代理模式 ————— 控制对象访问
- -Dmaven.multiModuleProjectDirectory system propery is not set. Check $M2_HOME environment variable a
- 函数练习
- 计算机系统漫游
- hdu1108-最小公倍数最大公约数
- 逆序对
- 打包压缩
- android 使用xml让animation按顺序播放
- java面试题汇总2