设计模式(四)状态模式 & 代理模式
状态模式
应用场景
制作一部糖果机,拥有投钱、退钱、检查糖果、发放糖果的程序。
方案一
在不同的操作下,先根据当前所在的状态作出对应的响应
public class GumballMachine1 {
final static int SOLD_OUT = 0;
final static int NO_QUARTER = 1;
final static int HAS_QUARTER = 2;
final static int SOLD = 3;
int state = SOLD_OUT;
int count = 0;
public GumballMachine(int count) {
this.count = count;
if (count > 0) {
state = NO_QUARTER;
}
}
public void insertQuarter() {
if (state == HAS_QUARTER) {
System.out.println("You can't insert another quarter");
} else if (state == NO_QUARTER) {
state = HAS_QUARTER;
System.out.println("You inserted a quarter");
} else if (state == SOLD_OUT) {
System.out.println("You can't insert a quarter, the machine is sold out");
} else if (state == SOLD) {
System.out.println("Please wait, we're already giving you a gumball");
}
}
public void ejectQuarter() {
if (state == HAS_QUARTER) {
System.out.println("Quarter returned");
state = NO_QUARTER;
} else if (state == NO_QUARTER) {
System.out.println("You haven't inserted a quarter");
} else if (state == SOLD) {
System.out.println("Sorry, you already turned the crank");
} else if (state == SOLD_OUT) {
System.out.println("You can't eject, you haven't inserted a quarter yet");
}
}
public void turnCrank() {
if (state == SOLD) {
System.out.println("Turning twice doesn't get you another gumball");
} else if (state == NO_QUARTER) {
System.out.println("You turned but there's no quarter");
} else if (state == SOLD_OUT) {
System.out.println("Your turned, but there are no gumballs");
} else if (state == HAS_QUARTER) {
System.out.println("You turned...");
state = SOLD;
dispense();
}
}
public void dispense() {
if (state == SOLD) {
System.out.println("A gumball comes rolling out the slot");
count = count - 1;
if (count == 0) {
System.out.println("Oops, out of gumballs");
state = SOLD_OUT;
} else {
state = NO_QUARTER;
}
} else if (state == NO_QUARTER) {
System.out.println("You need to pay first");
} else if (state == SOLD_OUT) {
System.out.println("No gumball dispensed");
} else if (state == HAS_QUARTER) {
System.out.println("No gumball dispensed");
}
}
}
基于方案一,当这个糖果机有新的需求,需要多加入一个“赢家”的状态,就要在每个方法中加入一个新的条件判断来处理,业务逻辑也会更加混乱。
方案二
定义一个状态接口,将每个具体的状态封装成一个类实现该接口
public interface State {
public void insertQuarter();
public void ejectQuarter();
public void turnCrank();
public void dispense();
}
public class NoQuarterState implements State {
GumballMachine2 gumballMachine;
public NoQuarterState(GumballMachine2 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 class WinnerState implements State {
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 class HasQuarterState implements State {
Random randomWinner = new Random(System.currentTimeMillis());
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 class GumballMachine2 {
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State winnerState;
State state = soldOutState;
int count = 0;
public GumballMachine(int numberGumballs) {
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
this.count = numberGumballs;
if (numberGumballs > 0) {
state = noQuarterState;
}
}
public void insertQuarter() {
state.insertQuarter();
}
public void ejectQuarter() {
state.ejectQuarter();
}
public void turnCrank() {
state.turnCrank();
state.dispense();
}
void setState(State state) {
this.state = state;
}
int getCount() {
return this.count;
}
void releaseBall() {
System.out.println("A gumball comes rolling out the slot");
if (count != 0) {
count = count - 1;
}
}
}
经过方案二的优化后,将状态封装成独立的类,并将动作委托到代表当前状态的对象。而整体代码则清晰简洁了,代码逻辑的划分更到位(局部化),更改代码方便且不易影响其它状态。
总结
状态模式允许对象基于内部状态而拥有不同的行为,在内部状态改变时改变它的行为,对象看起来好像修改了它的类。但状态模式是通过增加类的数目代价来获取弹性的。
延伸,和策略模式对比,主要是他们的意图不一样(他们的类图是一致的),策略模式通常会用行为或算法来配置Context类,状态模式允许Context随着状态改变而改变行为。
代理模式
应用场景
对糖果机的销售情况做监控
首先解释一下,这里的代理和Objective-C中所说的代理(Delegate)是有区别的,在下面的延伸部分再做详细说明。
//服务端的实现
import java.rmi.*;
import java.io.*;
import java.rmi.server.*;
public interface GumballMachineRemote extends Remote {
public int getCount() throws RemoteException; //因为是远程操作,所以得声明异常
public String getLocation() throws RemoteException;
public State getState() throws RemoteException;
}
public interface State extends Serializable { //只有扩展了序列化接口才可以在网络上传送
public void insertQuarter();
public void ejectQuarter();
public void turnCrank();
public void dispense();
}
public class NoQuarterState implements State {
transient GumballMachine3 gumballMachine; //为了不让GumballMachine加入到序列化,使用transient关键字修饰各个State实现类中的该实例变量
//... 其它方法的实现
}
//让GumballMachine继承UnicastRemoteObject,使其成为远程服务
public class GumballMachine3 extends UnicastRemoteObject implements GumballMachineRemote {
public GumballMachine(String location, int numberGumballs) throws RemoteException {
//...
}
//... GumballMachineRemote远程接口的实现,rmi系统会自动针对这些接口生成可被远程访问的方法
}
//客户端的实现
public class GumballMonitor {
GumballMachineRemote machine;
public GumballMonitor(GumballMachineRemote machine) {
this.machine = machine;
}
public void report() {
try {
System.out.println(machine.getLocation());
System.out.println(machine.getCount());
System.out.println(machine.getState());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
//测试程序
public class test1 {
void test() throws Exception {
try {
GumballMachineRemote machine = (GumballMachineRemote) Naming.lookup("rmi://xx.xx.com/gumballmachine");
GumballMonitor monitor = new GumballMonitor(machine);
monitor.report();
} catch (Exception e) {
e.printStackTrace();
}
}
}
这里,GumballMachineRemote就是一个远程代理。
总结
代理模式为另一个对象提供一个替身或占位符以访问这个对象,以便控制或管理访问。
延伸,代理还有很多变体,例如虚拟代理(控制访问实例化开销大的对象)、保护代理(基于调用者控制对象方法的访问)、防火墙代理、缓存代理等。
代理模式与装饰者模式的区别,装饰者模式只能装饰点缀,不会实例化任何对象,它是为对象加上行为,而代理则是通过创建代理对象控制访问。
Objective-C中的代理是指实现一个协议(接口)的对象,通过委托这个对象去执行协议中的方法,而无需关系该对象是什么,但它并没有是谁的替身或占位符的意思,也不是用于控制访问。
Java内置的代理支持可建立动态代理(反射),将调用分配到所选的处理器,下面再提供一个用动态代理的实现保护代理例子
public interface PersonBean {
void setName(String name);//想要控制别人不能设置,自己能设置
void setHotOrNotRating(int rating);//想要控制自己不能设置,别人能设置
}
public class PersonBeanImpl implements PersonBean {
String name;
int rating;
public void setName(String name) {
this.name = name;
}
public void setHotOrNotRating(int rating) {
this.rating += rating;
}
}
//import java.lang.reflect.*
public class OwnerInvocationHandler implements InvocationHandler {
PersonBean person;
public OwnerInvocationHandler(PersonBean person) {
this.person = person;
}
//动态代理的任何接口方法被调用时,会调用该invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {
try {
if (method.getName().startWith("get")) {
return method.invoke(person,args);
} else if (method.getName().equals("setHotOrNotRating")) {
throw new IllegalAccessException();
} else if (method.getName().startWith("set")) {
return method.invoke(person,args);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
public class NoOwnerInvocationHandler implements InvocationHandler {
PersonBean person;
public OwnerInvocationHandler(PersonBean person) {
this.person = person;
}
public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {
try {
if (method.getName().startWith("get")) {
return method.invoke(person,args);
} else if (method.getName().equals("setHotOrNotRating")) {
return method.invoke(person,args); //与OwnerInvocationHandler相反
} else if (method.getName().startWith("set")) {
throw new IllegalAccessException(); //与OwnerInvocationHandler相反
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
//这样就无需额外定义代理类,在使用时通过下面的Java内置代理支持方法获取动态的代理
public class test2 {
PersonBean getOwnerProxy(PersonBean person) {
return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(),person.getClass().getInterfaces(),new OwnerInvocationHandler(person));
}
PersonBean getNoOwnerProxy(PersonBean person) {
return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(),person.getClass().getInterfaces(),new NoOwnerInvocationHandler(person));
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 mingfungliu@gmail.com
文章标题:设计模式(四)状态模式 & 代理模式
文章字数:2.1k
本文作者:Mingfung
发布时间:2019-02-12, 13:12:00
最后更新:2019-02-12, 14:37:10
原始链接:http://blog.ifungfay.com/设计模式/设计模式(四)状态模式 & 代理模式/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。