【设计模式】学习笔记15:代理模式(Proxy Pattern)
本文出自 http://blog.csdn.net/shuangde800
走进代理模式
在上一篇的状态模式中,我们实现了一个糖果机GumballMachine, 客户通过投币可以买到糖果.
现在设想这样一种情况: 糖果机是属于某个糖果公司的,这个公司的CEO坐在办公室里喝茶,看报纸, 但他还想要随时了解糖果机的状态,
比如还剩下多少个糖果,卖了多少钱等等. CEO要怎样才能远程"遥控"糖果机获取信息呢?
这就得用到代理模式(Proxy Pattern)了.
可能我们会想到通过socket网络连接, 自己定义好协议去控制糖果机.这个其实就是代理模式的底层实现.
所谓代理(Proxy)就是代表某个真实的对象, 这个"代理"可以看作是真正的对象, 比如CEO在办公室中拿到了糖果机的"代理", 然后CEO
对"代理"进行的任何操作,到会"传送"到远程真实的糖果机上, 然后糖果机就会作出反应, 看起来好像"代理"就是真正的糖果机一样.
这一切的动作是利用网络和真正对象沟通的.
远程代理的角色
远程代理就好比"远程对象的本地代表".
何谓"远程对象"? 这是一种对象,活在不同的Java虚拟机(JVM)堆中(更一般的说法,在不同的地址空间运行的远程对象).
何谓"本地代表"? 这是一种可以由本地方法调用的对象,其行为会转发到远程对象中.
你的客户对象所做的就像是在做远程方法调用, 但其实只是调用本地堆中的"代理对象"上的方法,
再由代理处理所有网络通信的底层细节
JAVA远程方法调用(RMI)
如果要自己手动去实现网络通信的底层, 那么显然会大大加大编程复杂度, 幸运的是, Java中已经有内置工具给我们使用了.
这就是: Java远程方法调用(RMI: Remote Mehod Invocation)
用Java RMI创建远程服务的基本步骤:
1. 创建远程接口
// 这个接口扩展自Remote,支持远程调用
public interface MyRemote extends Remote{
// 每次远程调用都是“有风险的”,因为会设计网络和I/O
// 所以这个接口的方法都要声明异常
// 还要确定方法的返回值是primitive类型或者可序列化(Serializable)类型
// 远程方法的变量会被打包并通过网络运送,这要靠序列化来完成
// 自定义的类必须序列化
public String sayHello() throws RemoteException;
}
2. 实现远程接口
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{
// 声明默认构造器
protected MyRemoteImpl() throws RemoteException {
super();
}
// 实现接口
public String sayHello() throws RemoteException {
return "Hello, world!";
}
public static void main(String[] args) {
try {
MyRemote service = new MyRemoteImpl();
// 用Naming.rebind()绑定到remiregistry
Naming.rebind("rmi://127.0.0.1/RemoteHello", service);
} catch (Exception e){
e.printStackTrace();
}
}
}
3. 命令行编译上面两个类(PS. 用的是Ubuntu系统,不是用IDE写的,所以两个类也没有package,放在同一个目录下。实际上,如果直接用IDE编译会遇到一个问题,后面会提)
这样就把Remote.java 和RemoteImpl.java两个类都编译了,编译完后,产生两个编译文件:
MyRemote.class和MyRemoteImpl.java
4. 用rmic产生stub和skeleton
rmic是JDK内的一个工具,用来为一个服务类产生stub和skeleton。命名习惯是在远程实现的名字后面加上_Stub或_Skel。rmic有一些选项可以调整,包括不要产生skeleton,查看源代码,甚至使用IIOP作为协议。这个例子使用的是rmic常见的方式,将类产生在当前目录下(就是cd到的目录)。注意,rmic必须看到你的实现类,所以你可能会从你的远程实现所在的目录下执行rmic
执行完之后,发现目录下多了一个名为MyRemote_Stub.class的文件:
5. 执行rmiregistry
开启一个终端,启动rmiregistry
注意这个终端不要关闭!
6. 启动服务端服务
开启另一个终端,启动服务
这个终端也不要关闭。
---------------------------分割线------------------------------
上面是服务端的代码,下面来实现客户端的代码。
客户必须取得stub对象(我们的代理)以调用其中的方法。所以我们就需要RMI Registry的帮忙。客户从Registry中寻找(lookup)代理,就好像在电话簿里寻找一样,说:“我要找这个名字的stub”.
import java.rmi.*;
public class MyRemoteClient {
public static void main (String[] args) {
new MyRemoteClient().go();
}
public void go() {
try{
// 返回值是Object,所以要转换类型
// 需要ip地址或主机名,以及服务器绑定/重绑定时用的名称
MyRemote service = (MyRemote)Naming.lookup("rmi://127.0.0.1/RemoteHello");
// 看起来和一般的方法调用没有区别!(除了必须注意RemoteException之外)
String s = service.sayHello();
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
}
编译,并且运行,发现输出“Hello world!”了!
OK !这就是RMI的基本过程了,当然实际应用中肯定还会有些变化的。
对于RMI, 程序员常犯的三个错误:
1. 忘了在启动远程服务之前先启动rmiregistry(要用Naming.rebind()注册服务, rmiregistry必须是运行的)
2. 忘了让变量和返回值的类型成为可序列化的类型(这种错误无法在编译期发现, 只会在运行时发现)
3. 忘了给客户提供stub类.
在敲上面的例子时, 我用IDE(Eclipse)来写过,但是步骤3编译时,遇到的问题:
java.rmi.ConnectException: Connection refused to host: 127.0.0.1
我试了其它电脑,还换了系统实现上述代码, 编译时都遇到了这个问题。
有没有人知道告诉我一下?
定义代理模式
代理模式为另一个对象提供一个替身或占位符以控制这个对象的访问
- Subject, 为RealSubject和Proxy提供了接口, 通过实现同一个接口, Proxy在RealSubject出现的地方取代它
- RealSubject是真正做事的对象,它是被proxy代理和控制访问的对象.
- Proxy持有RealSubject的引用.在某些例子中, Proxy还会负责RealSubject对象的创建与销毁.客户和RealSubject的交互都必须通过Proxy.因为Proxy和RealSbuject实现相同的接口(Subject),所以任何用到RealSubject的地方,都可以用Proxy取代.Proxy也控制了对RealSubject的访问,在某些情况下,我们可能需要这样的控制.这些情况包括RealSubject是远程的对象, RealSubject创建开销大, 或RealSubject需要被保护.
代理模式的其他玩法
远程代理是一般代理模式的一种实现, 但是代理了模式的变体相当多
比如: 虚拟代理, 缓存代理,同步代理, 防火墙代理和写入时复制代理.
限于篇幅, 下次再继续学习
【设计模式】学习笔记15:代理模式(Proxy Pattern)的更多相关文章
- 设计模式系列之代理模式(Proxy Pattern)——对象的间接访问
说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...
- 乐在其中设计模式(C#) - 代理模式(Proxy Pattern)
原文:乐在其中设计模式(C#) - 代理模式(Proxy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 代理模式(Proxy Pattern) 作者:webabcd 介绍 为 ...
- 二十四种设计模式:代理模式(Proxy Pattern)
代理模式(Proxy Pattern) 介绍为其他对象提供一个代理以控制对这个对象的访问. 示例有一个Message实体类,某对象对它的操作有Insert()和Get()方法,用一个代理来控制对这个对 ...
- 设计模式 - 代理模式(proxy pattern) 未使用代理模式 具体解释
代理模式(proxy pattern) 未使用代理模式 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 部分代码參考: http://blog.csdn. ...
- 代理模式(Proxy pattern)
代理模式(proxy pattern):作用:为其他对象提供一种代理,以控制对这个对象的访问.代理对象在客户端对象和目标对象之间起中介的作用. 代理模式涉及到的角色: 抽象角色:声明真实对象和代理对象 ...
- 【转】设计模式(十一)代理模式Proxy(结构型)
设计模式(十一)代理模式Proxy(结构型) 1.概述 因为某个对象消耗太多资源,而且你的代码并不是每个逻辑路径都需要此对象, 你曾有过延迟创建对象的想法吗 ( if和else就是不同的两条逻辑路径) ...
- 设计模式——代理模式(Proxy Pattern)
代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问. UML图: 模型设计: Subject类: package com.cnblog.clarck; /** * Subject 类 ...
- 设计模式学习笔记--备忘录(Mamento)模式
写在模式学习之前 什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方式,这就是软件模式:每个模式描写叙述了一个在我们程序设计中常常发生的问题,以及该问题的解决方式:当我们碰到模 ...
- 13.代理模式(Proxy Pattern)
using System; namespace Test { //抽象角色:声明真实对象和代理对象的共同接口. //代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象, //同时代理 ...
- c#设计模式之代理模式(Proxy Pattern)
引言 代理这个词语,大家在现实世界已经频繁的接触过,例如火车站代理售票点,因为这些代理售票点的存在,我们不必要去火车站的售票处就可以查询或者取到火车票.代理点本身是没有能力生产车票的,我们在代理处享受 ...
随机推荐
- POJ 1321 棋盘问题 DFS 期末前水一水就好……
A - 棋盘问题 Time Limit:1000MS Memory Limit:10000KB 64bit IO Format:%I64d & %I64u Submit Sta ...
- ASP.NET 构建高性能网站 第4篇
部署优化 我们都知道,不同的部署方式对站点的性能是有影响的,可能有些朋友已经知道了这点,不管怎样,我们这里还是详细系统的讲述一下这个问题,熟悉的朋友权当回顾J. Release方式编译项目 如果我们的 ...
- Cocos2d-x移植android增加震动效果
cpp部分通过jni调用java静态函数 头文件: #include <jni.h> #include "cocos2d.h" #include "platf ...
- 盘点Linux内核源码中使用宏定义的若干技巧(1)
http://blog.chinaunix.net/uid-23769728-id-3141515.html
- 报错:不允许保存更改。您所做的更改要求删除并重新创建以下表……
在使用SQL Server 2008为某个表添加列的时候出现启用了"阻止保存要求重新创建表的更改问题的设置方法..."报错: 解决方法: 工具--选项--Designers-- ...
- MySql 数据库导入"Unknown command '\n'."错误解决办法
原文地址:http://www.cnblogs.com/bingxueme/archive/2012/05/15/2501999.html 在CMD 下 输入: Mysql -u root -p -- ...
- 记录一次apache错误:“child pid 29023 exit signal Segmentation fault (11)”
目前做了一台公网的测试机,主要是 php 5.3.3 版本,是 browser --> nginx --> apache --> php 今天因为想要安装一个商城,要求需要 P ...
- [Apache] Apache 從 2.2 換至 2.4 httpd.conf 的調整筆記 (windows 環境)
原文地址: http://www.dotblogs.com.tw/maplenote/archive/2012/07/20/apache24_httpd_conf.aspx 整理一下 Windows ...
- C语言变量的类型和存储位置
. C语言变量主要分为全局变量.静态全局变量.局部变量.静态局部变量和寄存器变量.其中静态变量用static关键字进行修饰.程序所占用的内存可以分为以下几个部分: ()代码段-存放程序代码,只读的,不 ...
- pythonUnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe5 in position 108: ordinal not in range(128
今天做网页到了测试和数据库交互的地方,其中HTML和数据库都是设置成utf-8格式编码,插入到数据库中是正确的,但是当读取出来的时候就会出错,原因就是Python的str默认是ascii编码,和uni ...