行为型模式:状态模式

十一大行为型模式之八:状态模式。简介姓名 :状态模式英文名 :State Pattern价值观 :有啥事让状态我来维护个人介绍 :Allow an object to alter its behavior when its internal state changes.The object will appear to change its class.当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。(来自《设计模式之禅》)你要的故事现在有好多个人贷款软件,比如:支付宝、360借条(打广告。。。)等等。贷款会有一个用户状态流程,游客->注册用户->授信用户->借款用户(这里简化了状态,只用 4 个)。每个状态拥有的权限不一样,如下图所示。从上图可以看到,一个用户有 3 种行为,分别是注册、授信、借款。当注册成功后,用户的状态就从『游客』改变为『注册用户』;当授信成功后,用户的状态就从『注册用户』改变为『授信用户』;当借款成功后,用户的状态就从『授信用户』改变为『借款用户』。现在我们就来实现用户注册、授信、借款的过程,因为每个状态的权限不一样,所以这里需要根据用户的状态来限制用户行为。很快,我们就完成下面的代码。class User { private String state; public String getState() { return state; } public void setState(String state) { this.state = state; } public void register() { if (“none”.equals(state)) { System.out.println(“游客。注册中。。。”); }else if (“register”.equals(state)) { System.out.println(“注册用户。不需要再注册。”); } else if (“apply”.equals(state)) { System.out.println(“授信用户。不需要再注册。”); } else if (“draw”.equals(state)) { System.out.println(“借款用户。不需要再注册。”); } } public void apply() { if (“none”.equals(state)) { System.out.println(“游客。不能申请授信。”); }else if (“register”.equals(state)) { System.out.println(“注册用户。授信申请中。。。”); } else if (“apply”.equals(state)) { System.out.println(“授信用户。不需要再授信。”); } else if (“draw”.equals(state)) { System.out.println(“借款用户。不需要再授信。”); } } public void draw(double money) { if (“none”.equals(state)) { System.out.println(“游客。申请借款【” + money + “】元。不能申请借款。”); } else if (“register”.equals(state)) { System.out.println(“注册用户。申请借款【” + money + “】元。还没授信,不能借款。”); } else if (“apply”.equals(state)) { System.out.println(“授信用户。申请借款【” + money + “】元。申请借款中。。。”); } else if (“draw”.equals(state)) { System.out.println(“授信用户。申请借款【” + money + “】元。申请借款中。。。”); } }}public class NoStateTest { public static void main(String[] args) { User user = new User(); user.setState(“register”); user.draw(1000); }}打印结果:注册用户。申请借款【1000.0】元。还没授信,不能借款。上面代码实现了用户 register (注册),apply (授信),draw (借款) 这 3 种行为,每个行为都会根据状态 state 来做权限控制。看起来有点繁琐,扩展性不高,假设新增了一个状态,那么注册、授信、借款这 3 种行为的代码都要修改。下面通过状态模式来解决这个问题。我们把状态给抽出来,作为一个接口,因为在每种状态中都可能有注册、授信、借款行为,所以把这 3 个行为作为状态接口的方法,让每个状态子类都实现相应的行为控制。如下代码所示。interface State { void register(); void apply(); void draw(double money);}/** * 游客 /class NoneState implements State { @Override public void register() { System.out.println(“游客。注册中。。。”); } @Override public void apply() { System.out.println(“游客。不能申请授信。”); } @Override public void draw(double money) { System.out.println(“游客。申请借款【” + money + “】元。不能申请借款。”); }}/* * 注册状态 /class RegisterState implements State { @Override public void register() { System.out.println(“注册用户。不需要再注册。”); } @Override public void apply() { System.out.println(“注册用户。授信申请中。。。”); } @Override public void draw(double money) { System.out.println(“注册用户。申请借款【” + money + “】元。还没授信,不能借款。”); }}/* * 授信状态 /class ApplyState implements State { @Override public void register() { System.out.println(“授信用户。不需要再注册。”); } @Override public void apply() { System.out.println(“授信用户。不需要再授信。”); } @Override public void draw(double money) { System.out.println(“授信用户。申请借款【” + money + “】元。申请借款中。。。”); }}/* * 借款状态 */class DrawState implements State { @Override public void register() { System.out.println(“借款用户。不需要再注册。”); } @Override public void apply() { System.out.println(“借款用户。不需要再授信。”); } @Override public void draw(double money) { System.out.println(“申请借款【” + money + “】元。申请借款中。。。”); }}class User1 { private State state; public State getState() { return state; } public void setState(State state) { this.state = state; } public void register() { this.state.register(); } public void apply() { this.state.apply(); } public void draw(double money) { this.state.draw(money); }}public class StateTest { public static void main(String[] args) { User1 user1 = new User1(); user1.setState(new RegisterState()); user1.apply(); user1.draw(1000); user1.setState(new ApplyState()); user1.draw(2000); }}打印结果:注册用户。授信申请中。。。注册用户。申请借款【1000.0】元。还没授信,不能借款。授信用户。申请借款【2000.0】元。申请借款中。。。看上面代码,我们抽象了 State 接口,4 种状态分别用 NoneState (游客)、RegisterState (注册)、ApplyState (授信)、DrawState (借款) 表示。而每个状态都有 3 种行为,它们各自对这些行为进行权限控制。这样子实现可以让权限逻辑分离开,分散到每个状态里面去,如果以后要业务扩展,要新增状态,那就很方便了,只需要再实现一个状态类就可以,不会影响到其他代码。这也是为什么《阿里巴巴 Java 开发手册》里面讲的,当超过 3 层的 if-else 的逻辑判断代码,推荐用状态模式来重构代码。总结状态模式 很好的减低了代码的复杂性,从而提高了系统的可维护性。在业务开发中可以尝试使用,比如在迭代开发中,业务逻辑越来越复杂,从而不得不使用很多 if-else 语句来实现时,就可以考虑一下是不是可以用 状态模式 来重构,特别是一些有状态流程转换方面的业务。看到这篇文章,想想工作中是不是有些复杂的代码可以重构,赶紧行动起来。推荐阅读:行为型模式:观察者模式行为型模式:迭代器模式行为型模式:策略模式设计模式系列文章持续更新中,欢迎关注公众号 LieBrother,一起交流学习。 ...

March 14, 2019 · 2 min · jiezi

设计模式手册之状态模式

什么是“状态模式”?状态模式:对象行为是基于状态来改变的。内部的状态转化,导致了行为表现形式不同。所以,用户在外面看起来,好像是修改了行为。Webpack4系列教程(17篇) + 设计模式手册(16篇):GitHub地址博客主题推荐:Theme Art Design,“笔记记录+搭建知识体系”的利器。原文地址: 设计模式手册之状态模式2. 优缺点优点封装了转化规则,对于大量分支语句,可以考虑使用状态类进一步封装。每个状态都是确定的,所以对象行为是可控的。缺点状态模式的关键是将事物的状态都封装成单独的类,这个类的各种方法就是“此种状态对应的表现行为”。因此,状态类会增加程序开销。3. 代码实现3.1 ES6 实现在JavaScript中,可以直接用JSON对象来代替状态类。下面代码展示的就是FSM(有限状态机)里面有3种状态:download、pause、deleted。控制状态转化的代码也在其中。DownLoad类就是,常说的Context对象,它的行为会随着状态的改变而改变。const FSM = (() => { let currenState = “download”; return { download: { click: () => { console.log(“暂停下载”); currenState = “pause”; }, del: () => { console.log(“先暂停, 再删除”); } }, pause: { click: () => { console.log(“继续下载”); currenState = “download”; }, del: () => { console.log(“删除任务”); currenState = “deleted”; } }, deleted: { click: () => { console.log(“任务已删除, 请重新开始”); }, del: () => { console.log(“任务已删除”); } }, getState: () => currenState };})();class Download { constructor(fsm) { this.fsm = fsm; } handleClick() { const { fsm } = this; fsm[fsm.getState()].click(); } hanldeDel() { const { fsm } = this; fsm[fsm.getState()].del(); }}// 开始下载let download = new Download(FSM);download.handleClick(); // 暂停下载download.handleClick(); // 继续下载download.hanldeDel(); // 下载中,无法执行删除操作download.handleClick(); // 暂停下载download.hanldeDel(); // 删除任务3.2 Python3 实现python的代码采用的是“面向对象”的编程,没有过度使用函数式的闭包写法(python写起来也不难)。因此,负责状态转化的类,专门拿出来单独封装。其他3个状态类的状态,均由这个状态类来管理。# 负责状态转化class StateTransform: def init(self): self.__state = ‘download’ self.__states = [‘download’, ‘pause’, ‘deleted’] def change(self, to_state): if (not to_state) or (to_state not in self.__states) : raise Exception(‘state is unvalid’) self.__state = to_state def get_state(self): return self.__state# 以下是三个状态类class DownloadState: def init(self, transfomer): self.__state = ‘download’ self.__transfomer = transfomer def click(self): print(‘暂停下载’) self.__transfomer.change(‘pause’) def delete(self): print(‘先暂停, 再删除’) class PauseState: def init(self, transfomer): self.__state = ‘pause’ self.__transfomer = transfomer def click(self): print(‘继续下载’) self.__transfomer.change(‘download’) def delete(self): print(‘删除任务’) self.__transfomer.change(‘deleted’)class DeletedState: def init(self, transfomer): self.__state = ‘deleted’ self.__transfomer = transfomer def click(self): print(‘任务已删除, 请重新开始’) def delete(self): print(‘任务已删除’)# 业务代码class Download: def init(self): self.state_transformer = StateTransform() self.state_map = { ‘download’: DownloadState(self.state_transformer), ‘pause’: PauseState(self.state_transformer), ‘deleted’: DeletedState(self.state_transformer) } def handle_click(self): state = self.state_transformer.get_state() self.state_map[state].click() def handle_del(self): state = self.state_transformer.get_state() self.state_map[state].delete()if name == ‘main’: download = Download() download.handle_click(); # 暂停下载 download.handle_click(); # 继续下载 download.handle_del(); # 下载中,无法执行删除操作 download.handle_click(); # 暂停下载 download.handle_del(); # 删除任务4. 参考23种设计模式全解析菜鸟教程状态模式《JavaScript设计模式与开发实践》

March 11, 2019 · 2 min · jiezi

python设计模式-状态模式

问题:有一个糖果公司需要设计一个糖果售卖机,控制流程如下图,需要怎么实现?这是一个状态图,每个圆圈都是一种状态。很明显,有有25分钱、 没有25分钱、 售出糖果、 糖果售罄四个状态,同时也对应四个动作:投入25分钱,退回25分钱,转动曲柄和发放糖果。那如何从状态图得到真正的代码呢?简单代码实现如下:#! -- coding: utf-8 --class GumballMachine: # 找出所有状态,并创建实例变量来持有当前状态,然后定义状态的值 STATE_SOLD_OUT = 0 STATE_NO_QUARTER = 1 STATE_HAS_QUARTER = 2 STATE_SOLD = 3 state = STATE_SOLD_OUT def init(self, count=0): self.count = count if count > 0: self.state = self.STATE_NO_QUARTER def str(self): return “Gumball machine current state: %s” % self.state def insert_quarter(self): # 投入25分钱 if self.state == self.STATE_HAS_QUARTER: # 如果已经投过 print(“You can’t insert another quarter”) elif self.state == self.STATE_NO_QUARTER: # 如果没有投过 self.state = self.STATE_HAS_QUARTER print(“You inserted a quarter”) elif self.state == self.STATE_SOLD_OUT: # 如果已经售罄 print(“You can’t insert a quarter, the machine is sold out”) elif self.state == self.STATE_SOLD: # 如果刚刚买了糖果 print(“Please wait, we’re already giving you a gumball”) def eject_quarter(self): # 退回25分 if self.state == self.STATE_HAS_QUARTER: print(“Quarter returned”) self.state = self.STATE_NO_QUARTER elif self.state == self.STATE_NO_QUARTER: print(“You haven’t inserted a quarter”) elif self.state == self.STATE_SOLD: print(“Sorry, you alread turned the crank”) elif self.state == self.SOLD_OUT: print(“You can’t eject, you haven’t inserted”) def turn_crank(self): # 转动曲柄 if self.state == self.STATE_SOLD: print(“Turning twice doesn’t get you another gumball”) elif self.state == self.STATE_NO_QUARTER: print(“You turned but there’s no quarter”) elif self.state == self.STATE_SOLD_OUT: print(“You turned, but there are no gumballs”) elif self.state == self.STATE_HAS_QUARTER: print(“You turned…”) self.state = self.STATE_SOLD self.dispense() def dispense(self): # 发放糖果 if self.state == self.STATE_SOLD: print(“A gumball comes rolling out the slot”) self.count -= 1 if self.count == 0: self.state = self.STATE_SOLD_OUT else: self.state = self.STATE_NO_QUARTER elif self.state == self.STATE_NO_QUARTER: print(“You need to pay first”) elif self.state == self.STATE_SOLD_OUT: print(“No gumball dispensed”) elif self.state == self.STATE_HAS_QUARTER: print(“No gumball dispensed”)if name == “main”: # 以下是代码测试 gumball_machine = GumballMachine(5) # 装入5 个糖果 print(gumball_machine) gumball_machine.insert_quarter() # 投入25分钱 gumball_machine.turn_crank() # 转动曲柄 print(gumball_machine) gumball_machine.insert_quarter() #投入25分钱 gumball_machine.eject_quarter() # 退钱 gumball_machine.turn_crank() # 转动曲柄 print(gumball_machine) gumball_machine.insert_quarter() # 投入25分钱 gumball_machine.turn_crank() # 转动曲柄 gumball_machine.insert_quarter() # 投入25分钱 gumball_machine.turn_crank() # 转动曲柄 gumball_machine.eject_quarter() # 退钱 print(gumball_machine)这段代码有几个问题:没有遵守开放-关闭原则更像是面向过程的设计状态转化被埋藏在条件语句中未来加入新的需求,需要改动的较多,不易维护,可能会出bug如何改进呢?考虑封装变化,把每个状态的行为都放在各自的类中,每个状态只要实现自己的动作,用加入新类的方式来实现新状态的加入。定义State 父类,在这个类中,糖果机的每个动作都有一个应对的方法为机器中的每个状态实现状态类,这些类将负责在对应的状态下进行机器的行为摆脱旧的条件代码,将动作委托到状态类新的实现代码如下:#! -- coding: utf-8 --class State: # 定义state基类 def insert_quarter(self): pass def eject_quarter(self): pass def turn_crank(self): pass def dispense(self): passclass SoldOutState(State): # 继承State 类 def init(self, gumball_machine): self.gumball_machine = gumball_machine def str(self): return “sold_out” def insert_quarter(self): print(“You can’t insert a quarter, the machine is sold out”) def eject_quarter(self): print(“You can’t eject, you haven’t inserted a quarter yet”) def turn_crank(self): print(“You turned, but ther are no gumballs”) def dispense(self): print(“No gumball dispensed”)class SoldState(State): # 继承State 类 def init(self, gumball_machine): self.gumball_machine = gumball_machine def str(self): return “sold” def insert_quarter(self): print(“Please wait, we’re already giving you a gumball”) def eject_quarter(self): print(“Sorry, you already turned the crank”) def turn_crank(self): print(“Turning twice doesn’t get you another gumball”) def dispense(self): self.gumball_machine.release_ball() if gumball_machine.count > 0: self.gumball_machine.state = self.gumball_machine.no_quarter_state else: print(“Oops, out of gumballs!”) self.gumball_machine.state = self.gumball_machine.soldout_stateclass NoQuarterState(State): # 继承State 类 def init(self, gumball_machine): self.gumball_machine = gumball_machine def str(self): return “no_quarter” def insert_quarter(self): # 投币 并且改变状态 print(“You inserted a quarter”) self.gumball_machine.state = self.gumball_machine.has_quarter_state def eject_quarter(self): print(“You haven’t insert a quarter”) def turn_crank(self): print(“You turned, but there’s no quarter”) def dispense(self): print(“You need to pay first”)class HasQuarterState(State): # 继承State 类 def init(self, gumball_machine): self.gumball_machine = gumball_machine def str(self): return “has_quarter” def insert_quarter(self): print(“You can’t insert another quarter”) def eject_quarter(self): print(“Quarter returned”) self.gumball_machine.state = self.gumball_machine.no_quarter_state def turn_crank(self): print(“You turned…”) self.gumball_machine.state = self.gumball_machine.sold_state def dispense(self): print(“No gumball dispensed”)class GumballMachine: def init(self, count=0): self.count = count # 找出所有状态,并创建实例变量来持有当前状态,然后定义状态的值 self.soldout_state = SoldOutState(self) self.no_quarter_state = NoQuarterState(self) self.has_quarter_state = HasQuarterState(self) self.sold_state = SoldState(self) if count > 0: self.state = self.no_quarter_state else: self.state = self.soldout_state def str(self): return “>>> Gumball machine current state: %s” % self.state def insert_quarter(self): # 投入25分钱 self.state.insert_quarter() def eject_quarter(self): # 退回25分 self.state.eject_quarter() # print(“state”, self.state, type(self.state)) def turn_crank(self): # 转动曲柄 # print(“state”, self.state, type(self.state)) self.state.turn_crank() def release_ball(self): # 发放糖果 print(“A gumball comes rolling out the slot…”) if self.count > 0: self.count -= 1 if name == “main”: # 以下是代码测试 gumball_machine = GumballMachine(5) # 装入5 个糖果 print(gumball_machine) gumball_machine.insert_quarter() # 投入25分钱 gumball_machine.turn_crank() # 转动曲柄 print(gumball_machine) gumball_machine.insert_quarter() #投入25分钱 gumball_machine.eject_quarter() # 退钱 gumball_machine.turn_crank() # 转动曲柄 print(gumball_machine) gumball_machine.insert_quarter() # 投入25分钱 gumball_machine.turn_crank() # 转动曲柄 gumball_machine.insert_quarter() # 投入25分钱 gumball_machine.turn_crank() # 转动曲柄 gumball_machine.eject_quarter() # 退钱 print(gumball_machine)重构后的代码相对于之前的代码做了哪些事情呢?将每个状态的行为局部话到自己的类中删除if 语句将状态类对修改关闭,对糖果季类对扩展开放下图是刚初始状态图示:上面重构部分代码使用的就是状态模式:定义状态模式: 状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。状态模式的类图如下:状态模式是将多个行为封装在状态对象中, context 的行为随时可委托到其中一个状态中。当前状态在不同的状态对象中改变,以反映出context 内部的状态,context 的行为也会随之改变。扩展如果,现在要在这四个状态的基础上再加一个状态(购买糖果后,有10%的概率再得一个),该如何实现呢?# 添加WinnerState 类,只有dispense 方法不同,可以从SoldState 类继承class WinnerState(SoldState): def str(self): return “winner” def dispense(self): print(“You’re a WINNER! You get two gumballs for your quarter”) self.gumball_machine.release_ball() if gumball_machine.count == 0: self.gumball_machine.state = self.gumball_machine.soldout_state else: self.gumball_machine.release_ball() if gumball_machine.count > 0: self.gumball_machine.state = self.gumball_machine.no_quarter_state else: print(“Oops, out of gumballs!”) self.gumball_machine.state = self.gumball_machine.soldout_state# 修改turn_crank 方法class HasQuarterState(State): … def turn_crank(self): print(“You turned…”) winner = random.randint(0, 9) if winner == 4 and self.gumball_machine.count > 1: # 如果库存大于 1 并且随机数等于4(可以是0到9任意值) self.gumball_machine.state = self.gumball_machine.winner_state else: self.gumball_machine.state = self.gumball_machine.sold_state# 在 GumballMachine 中初始化class GumballMachine: def init(self, count=0): self.count = count # 找出所有状态,并创建实例变量来持有当前状态,然后定义状态的值 … self.winner_state = WinnerState(self) …总结状态模式允许一个对象给予内部状态而拥有不同的行为状态模式用类代表状态Context 会将行为委托给当前状态对象通过将每状态封装进一个类,把改变局部化状态装欢可以由State 类或Context 类控制使用状态模式会增加类的数目状态类可以被多个Context 实例共享本文例子来自《Head First 设计模式》。最后,感谢女朋友支持和包容,比❤️也可以在公号输入以下关键字获取历史文章:公号&小程序 | 设计模式 | 并发&协程 ...

January 1, 2019 · 4 min · jiezi