Chapter 10 状态模式
假设需要这样一个自动糖果机:


就投入25分硬币的部分可以这样写:
public void insertQuarter(){
if(state==HAS_QUARTER){
// 已经投过了
}else if(state==SOLD_OUT){
// 售罄
}else if(state ==SOLD){
// 已经制作完给过顾客了,这个状态其实是中间态,然后会自动转换到NO_QUARTER
}else if(state==NO_QUARTER){
// 投入了25分正好,进入下个流程
}
}
看起来好像我们很好的处理了需求,但是如果用户增加需求说有10%的概率可以拿到一枚免费糖果,你可能就要增加一种状态,并在每个操作函数里面处理这种状态,非常不符合封装变化的需求。
不要维护我们现有的代码,我们重写它以便于将状态对象封装在各自的类中,然后在动作发生时委托给当前状态。
- 首先,我们定义一个State接口,在这个接口内,糖果机的每个动作都有一个对应的方法。
- 然后为机器中的每个状态实现状态类,这些类将负责在对应的状态下进行机器的行为。
- 最后,我们要摆脱旧的条件代码,取而代之的方法是,将动作委托到状态类。
现在我们要把一个状态的所有行为放在一个类中,这么一来我们将行为局部化了,并使得事情更容易改变和理解。
interface State{
void insertQuarter();
void ejectQuarter();
void turnCrank();
void dispense();
}
用state处理操作可以去掉 if else 判断的模式~
class NoQuarterState implements State{
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine){
this.gumballMachine=gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("如果有人投入了25分钱,我们就打印出一条消息,说我们接受了25分钱," +
"然后改变机器的状态到HasQuarterState");
gumballMachine.setState(gumballMachine.getHasQuarterState());
}
@Override
public void ejectQuarter() {
System.out.println("如果没给钱,就不要求退钱");
}
@Override
public void turnCrank() {
System.out.println("如果没给钱,就不能要求糖果");
}
@Override
public void dispense() {
System.out.println("如果没得到钱,我们就不能发放糖果。");
}
}
=====
class GumballMachine_ {
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State state = soldState;
int count = 0;
public GumballMachine_(int count) {
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldOutState = new SoldState(this);
this.count = count;
if (count > 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;
}
void releaseBall(){
System.out.println();
if(count!=0){
count=count-1;
}
}
public State getSoldState() {
return soldState;
}
public State getHasQuarterState() {
return hasQuarterState;
}
public State getNoQuarterState() {
return noQuarterState;
}
public State getSoldOutState() {
return soldOutState;
}
}
状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
state是可以被多个context共享的哈如果需要的话~

状态模式和策略模式的类图是一样的,但是策略模式其实不会改变持有的策略,一般都是在运行时固定一种;而状态模式相反,状态是不固定的,需要流转
- 这里为什么需要吧winnder(2颗糖) 和sold状态区分开作为两个类呢,明明内容都是类似的?
=> 因为一个类应该只有一种责任,以及如果商家要取消2颗糖这个策略的时候,分开的类你很好维护,状态很清晰,如果都放入sold,有多少种促销策略你的sold state就会有多复杂,任意一个有了变化你要考虑的修改就需要很谨慎。
策略模式:将可以互换的行为封装起来,然后使用委托决定使用哪一种行为
模板方法:由子类决定如何实现算法中的某些步骤
状态模式:封装基于状态的行为,并将行为委托给当前状态
Chapter 11 代理模式
假设要给糖果机加个监控,需要放在老板电脑上面运行,而糖果机的代码不在老板的电脑上,这要怎么将糖果机的实例对象传给监控呢?
这就需要远程调用了,也就是糖果机提供一个虚拟的接口给监控,然后监控调用这个虚拟糖果机,远程电脑上的实际糖果机就会运行。
远程代理就是远程对象的本地代表

这里其实就是客户调用了代理的方法,代理通过网络将方法调用传给实际对象,实际对象执行后再将结果通过网络返回给代理。
代理模式:为另一个对象提供一个替身或占位符控制这个对象的访问

- 特点:
让代理对象控制对象的访问,被代理对象可以是远程对象(远程代理),创建开销较大对象(虚拟代理),或需要安全控制的对象(保护代理)
为另一个对象提供代表,以便控制客户对对象的访问
=> 远程代理:好比远程对象(在不同JVM虚拟机的对象)的本地代表(本地方法的行为转移到远程),帮助我们处理网络上的细节
=> 虚拟代理:作为创建开销大的对象的代表
在大对象创建前和创建中由虚拟对象代替大对象,在创建完毕后将动作委派给大对象
=> 保护代理:通过代理实现不同的用户对对象有不同的访问权限
=> 动态代理:通过反射在运行过程中动态的创建代理,根据传入的接口集创建。Java内置动态代理支持
=> 防火墙代理:控制网络资源的访问,保护主题免于坏客户的侵害
=> 智能引用代理:当主题被引用时,进行额外的动作,例如记录独享被引用的次数
=> 缓存代理:为开销大的运算结果提供暂时存储;允许多个客户共享结果,减少计算或网络延迟
=> 同步代理:多线程下为主题提供安全访问
=> 复杂隐藏代理:用来隐藏一个类的复杂集合的复杂度
=> 写入时复制代理:用来控制对象的复制,延迟对象的复制,知道客户真的需要
🌰 这里举一个“虚拟代理”的例子,例如加载大图片的时候,需要很长时间加载,如果加载很久界面就会空白,所以可以创建一个图片加载的代理,当调用的时候先显示loading,同时异步加载图片真的返回以后再展示图片。
- 代理和装饰者的区别:代理是控制访问,而装饰者是为了增加功能,虽然两者都是把对象包起来。
- 代理和适配器的区别:代理提供相同接口,适配器提供不同接口
如果想让客户使用代理而非真正对象,可以结合工厂模式,这样只对外提供代理即可。
Chapter 12 复合模式
复合模式:结合两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题。如MVC

这里其实控制器可以理解为模型的observer,当模型内不发生变化,控制器可以收到通知来改变view例如disable某个按钮。
控制器的职责之一是接受view的输入,将用户输入设置到model上面,但是它的职责不仅仅是这样。即使controller只是这个责任,如果把改变model的职责给view,view和model就强耦合了,在其他场景如果想用view就不行了。



MVC中model对VC一无所知哦,他只是通知观察者,是完全解耦的。当然他是提供设置状态的接口的,让VC改变它。
Chapter 13 真实世界中的模式
- 模式是在某情境(context)下,针对某问题的某种解决方案。
情境:就是应用某个模式的情况。这应该是会不断出现的情况。
问题:就是你想在某情境下达到的目标,但也可以是某情境下的约束。
解决方案:就是你所追求的:一个通用的设计,用来解决约束、达到目标。
注意哦,模式要应用于重复出现的问题哈~

附录
可以参考https://www.jianshu.com/p/9079dd0013d8 或者 https://blog.csdn.net/roymuste/article/details/8539082
网友评论