设计模式之状态模式(三分钟学会一个设计模式)

状态模式(State Pattern)的定义是这样的:类的行为是基于它的状态改变的。
注意这里的状态不是狭义的指对象维护了一个“状态”字段,我们传入了不同的枚举值,对象整体的表现行为(对外方法)就改变了。
而是指内部的(任意)字段如果发生了变化,那么它的状态就变了,那么它对外的表现形式就变了。
它是面向对象的23种设计模式中的一种,属于行为模式的范围。
通常我们在解决不同状态下,对外方法的不同表现时,可以定义若干的枚举,然后写一大堆if、 elseif、 switch等选择命令来区分不同的状态,然后走不同的业务分支。
而状态模式是支持将这些分支业务抽离出一个独立类(状态类),我们通过传入不同的状态类,就可以动态的执行不同的业务方法。
整体的结构大概是这样的:

设计模式之状态模式(三分钟学会一个设计模式)

业务类维护了一个内部状态对象,这个状态对象支持由外部传入,切换为不同的状态对象。
而这些状态对象都统一实现了具体的方法,业务类内部在执行业务方法时,会调用这些状态对象中实现的方法。(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )这样在切换状态时,业务方法就会调用不同的状态对象的方法了。从面向对象的角度,实现了状态变化,类行为的同步变化。
来看一个具体的代码示例:

枚举类

1 package com.example.demo.learn.pattern.behavior.status; 2  3 public enum TextStatusEnum { 4     ONLY_READ, 5     READ_WRITE, 6     UNAVAILABLE; 7  8 }

状态定义接口

 1 package com.example.demo.learn.pattern.behavior.status;  2   3 /**  4  * @discription  5  */  6 public interface TextState {  7      TextStatusEnum getStatus();  8   9      void write(String content); 10  11      void clear(); 12  13      String read(); 14  15      void setContent(StringBuilder sb); 16 }

只读状态

 1 package com.example.demo.learn.pattern.behavior.status;  2   3 import lombok.Data;  4 import lombok.extern.slf4j.Slf4j;  5   6 /**  7  * @discription  8  */  9 @Slf4j 10 @Data 11 public class OnlyReadState implements TextState { 12     private static final TextStatusEnum textStatus = TextStatusEnum.ONLY_READ; 13  14     private StringBuilder sb; 15  16     @Override 17     public TextStatusEnum getStatus() { 18         return textStatus; 19     } 20  21     public void write(String content) { 22         log.error("sorry, you can not write"); 23     } 24  25     public void clear() { 26         log.error("sorry, you can not clear"); 27     } 28  29     public String read() { 30         return sb.toString(); 31     } 32  33     @Override 34     public void setContent(StringBuilder sb) { 35         this.sb = sb; 36     } 37 }

读写状态

 1 package com.example.demo.learn.pattern.behavior.status;  2   3 import lombok.Data;  4 import lombok.extern.slf4j.Slf4j;  5   6 /**  7  * @discription  8  */  9 @Data 10 @Slf4j 11 public class ReadWriteState implements TextState { 12     private static final TextStatusEnum textStatus = TextStatusEnum.ONLY_READ; 13  14     private StringBuilder sb = new StringBuilder(); 15  16     @Override 17     public TextStatusEnum getStatus() { 18         return textStatus; 19     } 20  21     public void write(String content) { 22         sb.append(content); 23     } 24  25     public void clear() { 26         sb.setLength(0); 27     } 28  29     public String read() { 30         return sb.toString(); 31     } 32  33     @Override 34     public void setContent(StringBuilder sb) { 35         this.sb = sb; 36     } 37 }

本文编辑器(业务类/上下文)

 1 package com.example.demo.learn.pattern.behavior.status;  2   3 import lombok.Data;  4 import lombok.extern.slf4j.Slf4j;  5   6 /**  7  * @discription  8  */  9 @Slf4j 10 public class TextEditor { 11  12     private StringBuilder sb = new StringBuilder(); 13  14     private TextState textState; 15  16     public void setState(TextState textState) { 17         textState.setContent(sb); 18         this.textState = textState; 19     } 20  21     public void write(String content) { 22         if (textState == null) { 23             log.error("no state exist"); 24             return; 25         } 26         textState.write(content); 27     } 28  29     public void clear() { 30         if (textState == null) { 31             log.error("no state exist"); 32             return; 33         } 34         textState.clear(); 35     } 36  37     public String read() { 38         if (textState == null) { 39             log.error("no state exist"); 40             return "no state"; 41         } 42         return textState.read(); 43     } 44  45 }

主类

 1 package com.example.demo.learn.pattern.behavior.status;  2   3 import lombok.extern.slf4j.Slf4j;  4   5 /**  6  * @discription  7  */  8 @Slf4j  9 public class PatternMain { 10     public static void main(String[] args) { 11         TextEditor editor = new TextEditor(); 12         String text; 13  14         //可读写状态 15         TextState rw = new ReadWriteState(); 16         editor.setState(rw); 17         for (int i = 0; i < 3; i++) { 18             editor.write("write" + i); 19             text = editor.read(); 20             log.warn("read :" + text); 21         } 22         editor.clear(); 23         text = editor.read(); 24         log.warn("after clear, we read :" + text); 25         editor.write("last write"); 26  27         log.warn("-----------------------now, we exchange state to only read-----------------------" ); 28         //只读状态 29         TextState or = new OnlyReadState(); 30         editor.setState(or); 31         for (int i = 0; i < 3; i++) { 32             editor.write("write" + i); 33             text = editor.read(); 34             log.warn("read :" + text); 35         } 36         editor.clear(); 37         text = editor.read(); 38         log.warn("after clear, we read :" + text); 39     } 40 }

输出效果如下:

10:02:52.356 [main] WARN com.example.demo.learn.pattern.behavior.status.PatternMain - read :write0 10:02:52.368 [main] WARN com.example.demo.learn.pattern.behavior.status.PatternMain - read :write0write1 10:02:52.369 [main] WARN com.example.demo.learn.pattern.behavior.status.PatternMain - read :write0write1write2 10:02:52.371 [main] WARN com.example.demo.learn.pattern.behavior.status.PatternMain - after clear, we read :(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ ) 10:02:52.372 [main] WARN com.example.demo.learn.pattern.behavior.status.PatternMain - -----------------------now, we exchange state to only read----------------------- 10:02:52.376 [main] ERROR com.example.demo.learn.pattern.behavior.status.OnlyReadState - sorry, you can not write 10:02:52.378 [main] WARN com.example.demo.learn.pattern.behavior.status.PatternMain - read :last write 10:02:52.378 [main] ERROR com.example.demo.learn.pattern.behavior.status.OnlyReadState - sorry, you can not write 10:02:52.378 [main] WARN com.example.demo.learn.pattern.behavior.status.PatternMain - read :last write 10:02:52.379 [main] ERROR com.example.demo.learn.pattern.behavior.status.OnlyReadState - sorry, you can not write 10:02:52.379 [main] WARN com.example.demo.learn.pattern.behavior.status.PatternMain - read :last write 10:02:52.379 [main] ERROR com.example.demo.learn.pattern.behavior.status.OnlyReadState - sorry, you can not clear 10:02:52.380 [main] WARN com.example.demo.learn.pattern.behavior.status.PatternMain - after clear, we read :last write  Process finished with exit code 0

我们可以看到在最初设置读写状态后,可以做读、写、清除等操作

在设置读状态后则只能读了。

这样回头来看,其实我们就是将不同if Switch的选择分支,连同选择的状态,一同封装到不同的状态类中,我们需要新增一种分支逻辑,不再需要修改选择分支,而是只需要新增一个状态类即可。
那是否状态模式可以替代传统的if 选择分支,答案是不能,本质上还是一个度的原因,面相对象如果过度设计,会导致类的数量无限膨胀,难以维护,试想如果存在多个状态字段(status、type等),则实体对象的状态是由多个状态字段组合而成的,每增加一个新的状态字段,都会导致状态的数量快速增加,这显然不是我们想看到的。

发表评论

评论已关闭。

相关文章