读<Head First>的时候,觉得这个书给人的感觉太好了,每一个设计模式都栩栩如生,这么好的书,光看不练习就浪费了,但是思来想去,似乎整理成博客,这样更好!就代理模式而言,书整整用了70页来书名代理模式,例子简直生动的不得了!希望我能用几篇博客,来将此思想整理出来,供大家,我自己来学习,当然还是希望大家能够买这个正版的书来看,O'REILLY的书一般都不会让人失望!
此处的代理模式包括,远程过程调用(RPC),动态代理等,所以这个系列的文章,有点儿长,故事的背景是一个糖果公司的CEO,一直不断的提需求,不断的找茬,看看聪明的程序员是怎样一点点收拾他的吧...
CEO的需求一 :希望我的糖果机能够获得更好的监控,你能找到一个办法给我一份库存以及机器的状态的报告么?
首先,定义一台水果机:
/**
* 糖果机
* Created by gzd on 2016/12/27.
*/
public class GumballMachine {
//位置
private String location;
//库存
private Integer count;
//状态
private String state;
public GumballMachine(String location,Integer count) {
this.location = location;
this.count = count;
}
public String getLocation() {
return location;
}
public GumballMachine setLocation(String location) {
this.location = location;
return this;
}
public Integer getInventory() {
return count;
}
public GumballMachine setInventory(Integer inventory) {
this.count = inventory;
return this;
}
public String getState() {
return state;
}
public GumballMachine setState(String state) {
this.state = state;
return this;
}
}
然后在定义一台糖果监视器,并提供report(方法)
/**
* 糖果监视器
* Created by gzd on 2016/12/27.
*/
public class GumballMonitor {
private GumballMachine gumballMachine;
public GumballMonitor(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void report(){
System.out.println("糖果机位置: " + gumballMachine.getLocation());
System.out.println("目前库存 : " + gumballMachine.getInventory());
System.out.println("当前状态 : " + gumballMachine.getState());
}
}
最后,CEO来验收,给他一个操作器,,他可以随时查看结果
public class GumballMachineTestDrive {
/**
* 输出结果:
* 糖果机位置: 东直门
* 目前库存 : 500
* 当前状态 : 售卖中...
* @param args
*/
public static void main(String[] args) {
GumballMachine machine = new GumballMachine("东直门", 500)
.setState("售卖中...");
GumballMonitor gumballMonitor = new GumballMonitor(machine);
gumballMonitor.report();
}
}
CEO看了这个说:这个机器的输出看起来不错,但是可能是我之前说的不清楚,我需要的是远程监控糖果机!其实我们已经把网络准备好了,但是你们在瞎忙什么?
这样,也就是CEO的需求二: 可以远程查看结果!
程序员开始讨论:
A:你说远程什么?
B:远程代理.你想想:我们已经写好监视器的代码,对吧?我们给GumballMonitor一个糖果机的引用,他给我们一份报告,问题在于监视器和糖果机在一起呢,也就是在一个jvm上执行,但是CEO希望在他的办公室就可以远程监控这些机器!所以,咱们不要变化GumballMonitor不要将糖果机教给GumballMonitor,而是将一个远程代理对象交给他!
A:我不太懂
B:其实所谓的代理(proxy),就是代表某个真实的对象,这个案例中,代理其实就是一个糖果机对象,但其实幕后是它利用网络和一个远程的糖果机进行沟通!
A:你是说,不该我们的代码,只是将GumballMachine代理版本教给监视器就可以了...然后这个代理假装他是真正的对象,但是其实一切的动作是它利用网络和真正的对象沟通!
B:差不多是这样的!
A:这好像说的比做的容易哦!
B:或许吧!但是我不认为有这么难,我们必须确定糖果机能够通过网络接收请求并提供服务;我们也需要让监视器有办法获得代理对象的引用,这方面,幸好java已经有了一些很棒的内置工具(RMI)可以帮我们,先看看远程代理....
远程代理就好比"远程对象的本地代表",何谓远程对象?这是一种对象,活在不同的java虚拟机堆中,何谓本地代表?这是一种可以由本地方法调用的对象,其行为会转发到远程对象中!
做之前,你需要了解,1,java的RMI;2如果把糖果机变成远程服务,提供一些可以被远程调用的方法;3创建一个能和远程糖果机沟通的代理,最后结合监视系统,CEO就可以看结果了,
用RMI远程服务,第一步,提供一个远程接口:
/**
* 必须继承Remote接口,提供一组可以远程调用的方法
* 确定所有方法的返回值,都是可序列化的
* Created by gzd on 2016/12/27.
*/
public interface GumballMachineRomote extends Remote {
int getCount() throws RemoteException;
String getLocation() throws RemoteException;
State getState() throws RemoteException;
}
State的代码如下:
/**
* Created by gzd on 2016/12/27.
*/
public interface State extends Serializable{
void insertQuarter();
void ejectQuarter();
void turnCrank();
void dispense();
}
todo state实现类
在前面我们已经做好了GumballMachine类,但是需要让他变成服务用,并且处理来自网络的请求,为了做这个,我们得让她实现上面的GumballMachineRomote接口.
/**
* 糖果机,可以处理远程任务
* Created by gzd on 2016/12/27.
*/
public class GumballMachine extends UnicastRemoteObject implements GumballMachineRomote{
//位置
private String location;
//库存
private Integer count;
//状态
private State state;
public GumballMachine(String location,Integer count) throws RemoteException {
this.location = location;
this.count = count;
}
@Override
public int getCount() throws RemoteException {
return count;
}
public String getLocation() {
return location;
}
public GumballMachine setLocation(String location) {
this.location = location;
return this;
}
public Integer getInventory() {
return count;
}
public GumballMachine setInventory(Integer inventory) {
this.count = inventory;
return this;
}
public State getState() {
return state;
}
public GumballMachine setState(State state) {
this.state = state;
return this;
}
}
糖果机上面的服务已经OK,现在,我们要将他装上去接受请求,首先,我们要确保他注册到RMI registry中,好让客户端找到他.GumballMachineTestDrive的代码要修改一下:
public class GumballMachineTestDrive {
public static void main(String[] args) throws AlreadyBoundException, MalformedURLException {
GumballMachineRemote machine = null;
try {
int count = 500;
String location = "192.168.31.149";
machine = new GumballMachine(location, count);
Naming.rebind("//" + location +"/gumballMachine",machine);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
下面开始测试,首先启动rmiregistry(去$JAVA_HOME/bin里找到他启动),然后启动我们的drive去注册!
至于GumballMonitor,也要做一下改动
/**
* 糖果监视器
* Created by gzd on 2016/12/27.
*/
public class GumballMonitor {
private GumballMachineRemote gumballMachine;
public GumballMonitor(GumballMachineRemote gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void report(){
try {
System.out.println("糖果机位置: " + gumballMachine.getLocation());
System.out.println("目前库存 : " + gumballMachine.getCount());
System.out.println("当前状态 : " + gumballMachine.getState());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
稍加改动drive,让CEO监视糖果机用:
public class GumballMachineTestDrive {
public static void main(String[] args) throws AlreadyBoundException, MalformedURLException, NotBoundException {
//创建一个机器的数组,条件有限,就写一个吧,假装他是多个,,,
String[] location = {"rmi://192.168.31.149/gumballMachine"};
//创建多个监视器
GumballMonitor[] monitor = new GumballMonitor[location.length];
for (int i = 0; i < location.length; i++) {
try {
GumballMachineRemote machine = (GumballMachineRemote) Naming.lookup(location[i]);
monitor[i] = new GumballMonitor(machine);
System.out.println(monitor[i]);
} catch (RemoteException e) {
e.printStackTrace();
}
for (int j = 0; j < monitor.length; j++) {
monitor[j].report();
}
}
}
}
好吧,,,就先到这里State类可以随便弄,,,嫌麻烦定义成String也可以,免去定义对象的麻烦,下一部分内容,将会介绍另一种代理,,,,,,动态代理,,,,