设计模式学习--代理模式(Proxy Pattern)
概述
———————————————————————————————————————————————————
代理模式—为另一对象提供替身或占位符以访问这个对象。
OO原则
———————————————————————————————————————————————————
- 封装变化
- 多用组合,少用继承
- 针对接口编程,不针对实现编程
- 为交互对象之间的松耦合设计而努力
- 类应该对扩展开放,对修改关闭
- 依赖抽象,不要以来具体类
- 只和朋友交谈
- 别找我,我会找你
- 类应该只有一个改变的理由
要点
———————————————————————————————————————————————————
- 代理模式为另一个对象提供代表,以便控制客户对对象访问,管理访问的方式有许多种。
- 远程代理管理客户和远程对象之间的交互。
- 虚拟代理控制访问实例化开销大的对象。
- 保护代理基于调用者控制对象方法的访问。
- 代理模式有许多变体,例如:缓存代理、同步代理、防火墙代理和写入时复制代理。
- 代理在结构上类似装饰者,但是目的不同。
- 装饰者模式为对象加上行为,而代理是控制访问。
- Java内置的代理支持,可以根据需要建立动态代理,并将所有调用分配到所选的处理器。
- 就和其他的包装者(wrapper)一样,代理会造成你的设计中类的数目增加。
例子:实现可以远程控制的糖果机
———————————————————————————————————————————————————
代码的改变可以对照“状态模式”那一篇博客
代理模式并不是那么容易理解,需要好好介绍一下
远程代理的角色
远程代理就好比“远程对象的本地代表”。何谓“远程对象”?这是一种对象,活在不同的Java虚拟机堆中(更一般的说法为,在不同的地址空间运行的远程对象)。何谓“本地代表”?这是一种可以由本地方法调用的对象,其行为会转发到远程对象中。
简单来说,代理就是一个本地对象,是客户直接打交道的对象,然而这个本地对象再跟真正的远程对象进行沟通,远程对象把请求的结果返回给代理,再由代理把结果返回给客户。这是远程控制的整个流程。
在本例子当中,糖果机充当的角色是真正的远程对象,是代理服务的对象。而代理呢,是要通过监视器从远程服务器中注册返回得到的。这里涉及到Java的RMI(远程方法调用),不是设计模式的内容,不多说。
我们要知道的就是,代理对象是通过RMI Registry得到的,它叫做stub(桩),stub扮演的是客户辅助对象,实在客户堆里的;然而在Java RMI中在服务器堆中skeleton(骨架)作为服务辅助对象。
不知道大家能不能看明白,其实在《Head First设计模式》当中,讲解得非常详细了,图文并茂,看几遍应该就能理解远程代理的操作流程是怎样的了。
来看看糖果机的远程代理实现吧
客户端里有:
GumballMonitor:这是我们的监视器代码,它使用代理来和远程糖果机沟通。
GumballStub:这是我们的代理
服务器端有:
GumballSkeleton:这是我们的服务器辅助对象
GumballMachine:这是我们的服务对象,它为客户暴露一个远程接口以供使用。
让GumballMachine准备好当一个远程服务
GumabllMachineRemote接口
[java] view plain
copy
print ?
- package gumballrmi;
-
- 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;
- }
package gumballrmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* 让GumballMachine准备好当一个远程服务
* @author wwj
* 代理模式的使用例子
*/
public interface GumballMachineRemote extends Remote {
public int getCount() throws RemoteException;
public String getLocation() throws RemoteException;
public State getState() throws RemoteException;
}
修改状态接口
[java] view plain
copy
print ?
- package gumballrmi;
-
- import java.io.*;
-
-
-
-
-
-
- public interface State extends Serializable {
-
- public void insertQuarter();
- public void ejectQuarter();
- public void turnCrank();
- public void dispense();
- }
package gumballrmi;
import java.io.*;
/**
* 2013/7/13
* @author wwj
* 扩展Serializable接口,则State的所有子类就可以在网络上传送了
*/
public interface State extends Serializable {
public void insertQuarter();
public void ejectQuarter();
public void turnCrank();
public void dispense();
}
修改状态实现类
[java] view plain
copy
print ?
- package gumballrmi;
-
- public class NoQuarterState implements State {
-
- transient GumballMachine gumballMachine;
-
- 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";
- }
- }
package gumballrmi;
public class NoQuarterState implements State {
//对于State的每个实现,我们都在GumballMachine实例变量前面加上transient关键字,这就就告诉JVM不要序列化这个字段
transient GumballMachine gumballMachine;
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";
}
}
HasQuarterState.java
[java] view plain
copy
print ?
- package gumballrmi;
-
- import java.util.Random;
-
- public class HasQuarterState implements State {
- Random randomWinner = new Random(System.currentTimeMillis());
- transient GumballMachine gumballMachine;
-
- public HasQuarterState(GumballMachine gumballMachine) {
- this.gumballMachine = gumballMachine;
- }
-
- public void insertQuarter() {
- System.out.println("You can't insert another quarter");
- }
-
- public void ejectQuarter() {
- System.out.println("Quarter returned");
- gumballMachine.setState(gumballMachine.getNoQuarterState());
- }
-
- public void turnCrank() {
- System.out.println("You turned...");
- int winner = randomWinner.nextInt(10);
- if((winner == 0) && (gumballMachine.getCount() > 1)) {
- gumballMachine.setState(gumballMachine.getWinnerState());
- } else {
- gumballMachine.setState(gumballMachine.getSoldState());
- }
- }
-
- public void dispense() {
- System.out.println("No gumball dispensed");
- }
-
- public String toString() {
- return "waiting for turn of crank";
- }
- }
package gumballrmi;
import java.util.Random;
public class HasQuarterState implements State {
Random randomWinner = new Random(System.currentTimeMillis()); //随机数产生器
transient GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("You can't insert another quarter");
}
public void ejectQuarter() {
System.out.println("Quarter returned");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
public void turnCrank() {
System.out.println("You turned...");
int winner = randomWinner.nextInt(10); //参数0~10的随机数
if((winner == 0) && (gumballMachine.getCount() > 1)) { //如果随机树为0,且足够的糖果的话,则可以得到两颗糖果
gumballMachine.setState(gumballMachine.getWinnerState());
} else {
gumballMachine.setState(gumballMachine.getSoldState());
}
}
public void dispense() {
System.out.println("No gumball dispensed");
}
public String toString() {
return "waiting for turn of crank";
}
}
SoldOutState.java
[java] view plain
copy
print ?
- package gumballrmi;
-
- public class SoldOutState implements State {
- transient GumballMachine gumballMachine;
-
- public SoldOutState(GumballMachine gumballMachine) {
- this.gumballMachine = gumballMachine;
- }
-
- public void insertQuarter() {
- System.out.println("You can't insert a quarter, the machine is sold out");
- }
-
- public void ejectQuarter() {
- System.out.println("You can't eject, you haven't inserted a quarter yet");
- }
-
- public void turnCrank() {
- System.out.println("You turned, but there are no gumballs");
- }
-
- public void dispense() {
- System.out.println("No gumball dispensed");
- }
-
- public String toString() {
- return "sold out";
- }
- }
package gumballrmi;
public class SoldOutState implements State {
transient GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("You can't insert a quarter, the machine is sold out");
}
public void ejectQuarter() {
System.out.println("You can't eject, you haven't inserted a quarter yet");
}
public void turnCrank() {
System.out.println("You turned, but there are no gumballs");
}
public void dispense() {
System.out.println("No gumball dispensed");
}
public String toString() {
return "sold out";
}
}
SoldState.java
[java] view plain
copy
print ?
- package gumballrmi;
-
- public class SoldState implements State {
-
- transient GumballMachine gumballMachine;
-
- public SoldState(GumballMachine gumballMachine) {
- this.gumballMachine = gumballMachine;
- }
-
- public void insertQuarter() {
- System.out.println("Please wait, we're already giving you a gumball");
- }
-
- public void ejectQuarter() {
- System.out.println("Sorry, you already turned the crank");
- }
-
- public void turnCrank() {
- System.out.println("Turning twice doesn't get you another gumball!");
- }
-
- public void dispense() {
- gumballMachine.releaseBall();
- if (gumballMachine.getCount() > 0) {
- gumballMachine.setState(gumballMachine.getNoQuarterState());
- } else {
- System.out.println("Oops, out of gumballs!");
- gumballMachine.setState(gumballMachine.getSoldOutState());
- }
- }
-
- public String toString() {
- return "dispensing a gumball";
- }
- }
package gumballrmi;
public class SoldState implements State {
transient GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("Please wait, we're already giving you a gumball");
}
public void ejectQuarter() {
System.out.println("Sorry, you already turned the crank");
}
public void turnCrank() {
System.out.println("Turning twice doesn't get you another gumball!");
}
public void dispense() {
gumballMachine.releaseBall();
if (gumballMachine.getCount() > 0) {
gumballMachine.setState(gumballMachine.getNoQuarterState());
} else {
System.out.println("Oops, out of gumballs!");
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
public String toString() {
return "dispensing a gumball";
}
}
WinnerState.java
[java] view plain
copy
print ?
- package gumballrmi;
-
- public class WinnerState implements State {
- transient GumballMachine gumballMachine;
-
- public WinnerState(GumballMachine gumballMachine) {
- this.gumballMachine = gumballMachine;
- }
-
- public void insertQuarter() {
- System.out.println("Please wait, we're already giving you a Gumball");
- }
-
- public void ejectQuarter() {
- System.out.println("Please wait, we're already giving you a Gumball");
- }
-
- public void turnCrank() {
- System.out.println("Turning again doesn't get you another gumball!");
- }
-
- public void dispense() {
- System.out.println("YOU'RE A WINNER! You get two gumballs for your quarter");
- gumballMachine.releaseBall();
- if (gumballMachine.getCount() == 0) {
- gumballMachine.setState(gumballMachine.getSoldOutState());
- } else {
- gumballMachine.releaseBall();
- if (gumballMachine.getCount() > 0) {
- gumballMachine.setState(gumballMachine.getNoQuarterState());
- } else {
- System.out.println("Oops, out of gumballs!");
- gumballMachine.setState(gumballMachine.getSoldOutState());
- }
- }
- }
-
- public String toString() {
- return "despensing two gumballs for your quarter, because YOU'RE A WINNER!";
- }
- }
package gumballrmi;
public class WinnerState implements State {
transient GumballMachine gumballMachine;
public WinnerState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("Please wait, we're already giving you a Gumball");
}
public void ejectQuarter() {
System.out.println("Please wait, we're already giving you a Gumball");
}
public void turnCrank() {
System.out.println("Turning again doesn't get you another gumball!");
}
public void dispense() {
System.out.println("YOU'RE A WINNER! You get two gumballs for your quarter");
gumballMachine.releaseBall();
if (gumballMachine.getCount() == 0) {
gumballMachine.setState(gumballMachine.getSoldOutState());
} else {
gumballMachine.releaseBall();
if (gumballMachine.getCount() > 0) {
gumballMachine.setState(gumballMachine.getNoQuarterState());
} else {
System.out.println("Oops, out of gumballs!");
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
}
public String toString() {
return "despensing two gumballs for your quarter, because YOU'RE A WINNER!";
}
}
监视器
[java] view plain
copy
print ?
- package gumballrmi;
-
- import java.rmi.RemoteException;
-
-
-
-
-
-
-
-
- public class GumballMonitor {
- GumballMachineRemote gumballMachine;
-
- public GumballMonitor(GumballMachineRemote gumballMachine) {
- this.gumballMachine = gumballMachine;
- }
-
- public void report() {
- try {
- System.out.println("Gumball Machine: " + gumballMachine.getLocation());
- System.out.println("Current inventory: " + gumballMachine.getCount() + " gumballs");
- System.out.println("Current State: " + gumballMachine.getState());
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
package gumballrmi;
import java.rmi.RemoteException;
/**
*
* @author wwj
* 糖果监视器,以便取得机器的位置、糖果的库存量以及当前机器的状态
* 并打印一份可爱的报告
* 这是我们的监视器代码,它使用代理和远程糖果机沟通
*/
public class GumballMonitor {
GumballMachineRemote gumballMachine;
public GumballMonitor(GumballMachineRemote gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void report() {
try {
System.out.println("Gumball Machine: " + gumballMachine.getLocation());
System.out.println("Current inventory: " + gumballMachine.getCount() + " gumballs");
System.out.println("Current State: " + gumballMachine.getState());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
糖果机
package gumballrmi;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
/**
* 2013/7/13
* @author wwj
* 糖果机成为一个远程服务
* 代理模式:
*/
public class GumballMachine extends UnicastRemoteObject implements GumballMachineRemote{
State soldOutState; //售空状态
State noQuarterState; //没有投入25分钱
State hasQuarterState; //有25分钱了
State soldState; //出售状态
State winnerState; //10%的中奖率
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 String getLocation() {
return 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;
}
public State getState() {
return state;
}
void releaseBall() {
System.out.println("A gumball comes rolling out the slot...");
if(count != 0) {
count = count - 1;
}
}
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 int getCount() {
return count;
}
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中注册。。。。。
[java] view plain
copy
print ?
- package gumballrmi;
-
- import java.rmi.Naming;
-
- public class GumballMachineTestDrive {
-
- public static void main(String[] args) {
- GumballMachineRemote gumballMachine = null;
- int count = 0;
-
- 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();
- }
- }
- }
package gumballrmi;
import java.rmi.Naming;
public class GumballMachineTestDrive {
public static void main(String[] args) {
GumballMachineRemote gumballMachine = null;
int count = 0;
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();
}
}
}
以上也就是制作远程服务的步骤
步骤一:制作远程接口
步骤二:制作远程的实现
步骤三:利用rmic产生的stub和skeleton
步骤四:启动RMI registry
步骤五:开始远程服务
在代理模式中还有虚拟代理和保护代理,在这里就只讨论远程代理,是用得比较多的一种,其他的可以参考《Head First 设计模式》。