Head First 設計模式第十章-狀態模式 狀態模式(headcount)

      網友投稿 657 2025-03-31

      狀態模式


      策略模式和狀態模式是雙胞胎,在出生時才分開。你已經知道,策略模式是圍繞可以互換的算法來創建成功業務的,然而,狀態走的是更崇高的路,它通過改變對象內部的狀態來幫助對象控制自己的行為。

      定義狀態模式

      先看看定義:狀態模式允許對象在內部狀態改變時改變它的行為,對象看起來好像修改了它的類

      例題

      自動糖果售賣機,糖果機的控制器需要的工作流程如下圖

      從上面的狀態圖中可以找到所有的狀態:

      我們可以創建一個實例變量來持有目前的狀態,然后定義每個狀態的值:

      1

      2

      3

      4

      5

      6

      7

      //每個狀態用不同的值表示

      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;

      現在,我們將所有系統中可以發生的動作整合起來:

      “投入25分錢”,“退回25分錢”,“轉動曲柄”,“發放糖果”

      這些動作是糖果機的接口,這是你能對糖果機做的事情,

      調用任何一個動作都會造成狀態的轉換,

      發放糖果更多是糖果機的內部動作,機器自己調用自己。

      我們創建一個類,它的作用就像是一個狀態機,每一個動作,我們都創建了一個對應的方法,這些方法利用條件語句來決定在每個狀態內什么行為是恰當的。比如對“投入25分錢”這個動作來說,我們可以把對應方法寫成下面的樣子:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      public void insertQuarter(){

      if(state==HAS_QUARTER){

      //每個狀態對應的行為

      ......

      }else if(state==SOLD_OUT){

      ......

      }else if(state ==SOLD){

      ......

      }else if(state==NO_QUARTER){

      state=HAS_QUARTER;//狀態轉換

      ......

      }

      }

      初步代碼

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      68

      69

      70

      71

      72

      73

      74

      75

      76

      77

      class GumballMachine{

      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;

      }

      }

      //當有25分錢投入,就會執行這個方法

      public void insertQuarter(){

      if(state==HAS_QUARTER){

      System.out.println("如果已投入過25分錢,我們就告訴顧客");

      }else if(state==NO_QUARTER){

      state=HAS_QUARTER;

      System.out.println("如果是在“沒有25分錢”的狀態下,我們就接收25分錢," +"并將狀態轉換到“有25分錢”的狀態");

      }else if(state ==SOLD_OUT){

      System.out.println("如果糖果已經售罄,我們就拒絕收錢");

      }else if(state==SOLD){

      System.out.println("如果顧客剛才買了糖果,就需要稍等一下,好讓狀態轉換完畢。" +"恢復到“沒有25分錢”的狀態");

      state=NO_QUARTER;

      }

      }

      //如果顧客試著退回25分錢就執行這個方法

      public void ejectQuarter(){

      if(state==HAS_QUARTER){

      System.out.println("如果有25分錢,我們就把錢退出來,回到“沒有25分錢”的狀態");

      state=NO_QUARTER;

      }else if(state==NO_QUARTER){

      System.out.println("如果沒有25分錢的話,當然不能退出25分錢");

      }else if(state ==SOLD){

      System.out.println("顧客已經轉動曲柄就不能再退錢了,他已經拿到糖果了");

      }else if(state==SOLD_OUT){

      System.out.println("如果糖果售罄,就不能接受25分錢,當然也不可能退錢");

      }

      }

      //顧客試著轉動曲柄

      public void turnCrank(){

      if(state==SOLD){

      System.out.println("別想騙過機器拿兩次糖果");

      }else if(state==NO_QUARTER){

      System.out.println("我們需要先投入25分錢");

      }else if(state ==SOLD_OUT){

      System.out.println("我們不能給糖果,已經沒有任何糖果了");

      }else if(state==HAS_QUARTER){

      System.out.println("成功,他們拿到糖果了," +"改變狀態到“售出糖果”然后調用機器的disoense()方法");

      state=SOLD;

      dispense();

      }

      }

      //調用此方法,發放糖果

      public void dispense(){

      if(state==SOLD){

      System.out.println("我們正在“出售糖果”狀態,給他們糖果");

      count=count-1;

      /*

      我們在這里處理“糖果售罄”的情況,如果這是最后一個糖果,將機器的狀態設置到“糖果售罄”否則就回到“沒有25分錢”的狀態

      */

      if(count==0){

      System.out.println();

      state=SOLD_OUT;

      }else{

      state=NO_QUARTER;

      }

      }else if(state==SOLD_OUT){

      System.out.println("這些都不應該發生,但是如果做了,就得到錯誤提示");

      }else if(state ==HAS_QUARTER){

      System.out.println("這些都不應該發生,但是如果做了,就得到錯誤提示");

      }else if(state==NO_QUARTER){

      System.out.println("這些都不應該發生,但是如果做了,就得到錯誤提示");

      }

      }

      }

      盡管程序完美運行,但還是躲不掉需求變更的命運

      現在糖果公司要求:當曲柄被轉動時,有10%的幾率掉下來的是兩個糖果。(氪金扭蛋)

      再回看一下我們的初步代碼,想要實現新的需求將會變得非常麻煩:

      必須新增一個中獎的“贏家”狀態。

      必須在每一個方法添加新的判斷條件來處理“贏家”狀態。

      轉動把手的方法中還需要檢查目前狀態是否是“贏家”再決定切換到“贏家”狀態行為還是正常出售行為。

      在現有代碼基礎上做增加將會很麻煩,也不利與以后的維護,擴展性差。

      回顧一下第一章的策略模式中的設計原則:

      找出應用中可能需要變化之處,把他們獨立出來

      將狀態獨立出來,封裝成一個類,都實現State接口,類圖如下:

      新的設計想法如下:

      首先,我們定義一個State接口,在這個接口內,糖果機的每個動作都有一個對應的方法

      然后為機器的每個狀態實現狀態類,這些類將負責在對應的狀態下進行機器的行為

      最后,我們要擺脫舊的條件代碼,取而代之的方式是,將動作委托到狀態類

      代碼

      定義一個State接口

      1

      2

      3

      4

      5

      6

      public interface State {

      public void insertQuarter();//投幣

      public void ejectQuarter();//退幣

      public void turnCrank();//轉動出貨把手

      public void dispense();//出售

      }

      為機器的每個狀態實現狀態類:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      68

      69

      70

      71

      72

      73

      74

      75

      76

      77

      78

      79

      80

      81

      82

      83

      84

      85

      86

      87

      88

      89

      90

      91

      92

      93

      94

      95

      96

      97

      98

      99

      100

      101

      102

      103

      104

      105

      106

      107

      108

      109

      110

      111

      112

      113

      114

      115

      116

      117

      118

      119

      120

      121

      122

      123

      124

      125

      //未投幣狀態

      public class NoQuarterState?implements State {

      GumballMachine gumballMachine;

      public NoQuarterState(GumballMachine gumballMachine) {

      this.gumballMachine=gumballMachine;

      }

      public void insertQuarter() {

      System.out.println("你投入一枚硬幣");

      gumballMachine.setState(gumballMachine.getHasQuarterState());//狀態轉換為已投幣狀態

      }

      public void ejectQuarter() {

      System.out.println("你未投幣,無法退錢");

      }

      public void turnCrank() {

      System.out.println("未投幣,請先投幣");

      }

      public void dispense() {

      System.out.println("請先投幣");

      }

      }

      //已投幣狀態

      public class HasQuarterState?implements State {

      Random randomWinner=new Random(System.currentTimeMillis());

      GumballMachine gumballMachine;

      public HasQuarterState(GumballMachine gumballMachine) {

      this.gumballMachine=gumballMachine;

      }

      public void insertQuarter() {

      System.out.println("已投幣,無法再接收投幣");

      }

      public void ejectQuarter() {

      System.out.println("已退幣");

      gumballMachine.setState(gumballMachine.getNoQuarterState());

      }

      public void turnCrank() {

      System.out.println("已轉動把手,糖果出售中。。。。");

      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("機器中已經沒有糖果可以出售了!");

      }

      }

      //出售狀態

      public class SoldState?implements State {

      GumballMachine gumballMachine;

      public SoldState(GumballMachine gumballMachine) {

      this.gumballMachine=gumballMachine;

      }

      public void insertQuarter() {

      System.out.println("請等候,正在初始化機器中");

      }

      public void ejectQuarter() {

      System.out.println("抱歉,您已轉動把手獲得了糖果,無法退幣");

      }

      public void turnCrank() {

      System.out.println("您重復轉動把手,無法再獲取更多糖果");

      }

      public void dispense() {

      gumballMachine.releaseBall();//出貨,糖果-1

      if(gumballMachine.getCount()>0)

      gumballMachine.setState(gumballMachine.getNoQuarterState());

      else {

      System.out.println("糖果已售完");

      gumballMachine.setState(gumballMachine.getSoldOutState());

      《Head First 設計模式》第十章-狀態模式 狀態模式(headcount)

      }

      }

      }

      //售罄狀態

      public class SoldOutState?implements State {

      GumballMachine gumballMachine;

      public SoldOutState(GumballMachine gumballMachine) {

      this.gumballMachine=gumballMachine;

      }

      public void insertQuarter() {

      System.out.println("此機器的糖果已售完,不接收投幣");

      }

      public void ejectQuarter() {

      System.out.println("未投幣,退幣失敗");

      }

      public void turnCrank() {

      System.out.println("糖果已售完,轉動把手也不會有糖果出來的");

      }

      public void dispense() {

      System.out.println("機器中已無糖果");

      }

      }

      //贏家狀態

      public class WinnerState?implements State {

      GumballMachine gumballMachine;

      public WinnerState(GumballMachine gumballMachine) {

      this.gumballMachine=gumballMachine;

      }

      public void insertQuarter() {

      System.out.println("請等候,正在初始化機器中");

      }

      public void ejectQuarter() {

      System.out.println("抱歉,您已轉動把手獲得了糖果");

      }

      public void turnCrank() {

      System.out.println("您重復轉動把手,無法再獲取更多糖果");

      }

      public void dispense() {

      System.out.println("恭喜你成為幸運兒,你將額外獲得一個免費糖果");

      gumballMachine.releaseBall();//出貨,糖果-1

      if(gumballMachine.getCount()==0)

      gumballMachine.setState(gumballMachine.getSoldOutState());

      else {

      gumballMachine.releaseBall();

      if(gumballMachine.getCount()>0)

      gumballMachine.setState(gumballMachine.getNoQuarterState());

      else {

      System.out.println("糖果已售完");

      gumballMachine.setState(gumballMachine.getSoldOutState());

      }

      }

      }

      }

      糖果機類:

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      68

      public class GumballMachine {

      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);

      winnerState=new WinnerState(this);

      this.count=numberGumballs;

      if(numberGumballs>0)

      state=noQuarterState;//先判斷條件再改變狀態

      }

      //將動作委托到狀態類

      public void insterQuarter() {

      state.insertQuarter();

      }

      public void ejectQuarter() {

      state.ejectQuarter();

      }

      public void turnCrank() {

      state.turnCrank();

      state.dispense();

      }

      //獲取當前狀態

      public State getHasQuarterState() {

      return hasQuarterState;

      }

      //改變狀態

      public void setState(State state) {

      this.state=state;

      }

      public void releaseBall() {

      System.out.println("糖果從出口售出");

      if(count!=0)

      count-=1;

      }

      public State getSoldOutState() {

      return soldOutState;

      }

      public State getNoQuarterState() {

      return noQuarterState;

      }

      public State getSoldState() {

      return soldState;

      }

      //獲取糖果機中糖果數量

      public int getCount() {

      return count;

      }

      public State getWinnerState() {

      return winnerState;

      }

      public String toString() {

      // TODO 自動生成的方法存根

      String s="剩余糖果:"+count;

      return s;

      }

      }

      以上就是用狀態模式實現的,仔細觀察你會發現狀態模式其實和策略模式很像,來看看狀態模式的類圖:

      狀態模式的類圖其實和策略模式完全一樣!

      狀態模式與策略模式

      這兩個模式的差別在于它們的“意圖”

      以狀態模式而言,我們將一群行為封裝在狀態對象中,context的行為隨時可委托到那些狀態對象中的一個,隨著時間而流逝,當前狀態在狀態對象集合中游走改變,以反映出context內部的狀態,因此,context的行為也會跟著改變,但是context的客戶對于狀態對象了解不多,甚至根本是渾然不覺。

      以策略模式而言,客戶通常主動指定Context所要組合的策略對象時哪一個。現在,固然策略模式讓我們具有彈性,能夠在運行時改變策略,但對于某個context對象來說,通常都只有一個最適當的策略對象。

      一般的,我們把策略模式想成是除了繼承之外的一種彈性替代方案,如果你使用繼承定義了一個類的行為,你將被這個行為困住,是指要修改它都很難,有了策略模式,你可以通過組合不同的對象來改變行為。

      我們把狀態模式想成是不用在context中放置許多條件判斷的替代方案,通過將行為包裝進狀態對象中,你可以通過在context內簡單地改變狀態對象來改變context的行為。

      模式區分

      狀態模式:封裝基于狀態的行為,并將行為委托到當前狀態

      策略模式:將可以互換的行為封裝起來。然后使用委托的方法,覺得使用哪一個行為

      模板方法模式:由子類決定如何實現算法中的某些步驟

      要點

      (1)狀態模式允許一個對象基于內部狀態而擁有不同的行為。

      (2)和程序狀態機(PSM)不同,狀態模式用類來表示狀態。

      (3)Context會將行為委托給當前狀態對象。

      (4)通過將每一個狀態封裝進一個類,我們把以后需要做的任何改變局部化了。

      (5)狀態模式和策略模式有相同的類圖,但是他們的意圖不同。

      (6)策略模式通常會用行為或算法配置Context類。

      (7)狀態模式允許Context隨著狀態的改變而改變行為。

      (8)狀態轉換可以有State類或Context類控制。

      (9)使用狀態模式通常會導致設計中類的數目大量增加。

      (10)狀態欄可以被多個Context實例共享。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:WPS表格辦公—根據單元格顏色進行排序(wps工作表格怎么按顏色排序)
      下一篇:碼農,有趣的靈魂....
      相關文章
      亚洲AV无码一区二区大桥未久 | 国内精品久久久久影院亚洲| 亚洲国产婷婷六月丁香| 亚洲另类春色校园小说| 亚洲国产精品热久久| 无码乱人伦一区二区亚洲| 久久久久久久久亚洲 | 亚洲avav天堂av在线网爱情| 亚洲VA成无码人在线观看天堂| 亚洲综合区小说区激情区| 亚洲另类少妇17p| 亚洲毛片网址在线观看中文字幕| 亚洲中文无韩国r级电影| 自拍偷自拍亚洲精品情侣| 亚洲免费观看视频| 精品国产综合成人亚洲区| 亚洲免费在线视频| 亚洲图片校园春色| 亚洲一卡2卡3卡4卡5卡6卡 | 亚洲欧洲无码AV不卡在线| 亚洲国产精品成人综合色在线| 亚洲av无码专区首页| 亚洲成a人片在线观看国产| 亚洲中文字幕成人在线| 亚洲精品无码久久一线| 亚洲精品在线观看视频| 亚洲国产精品白丝在线观看| 亚洲已满18点击进入在线观看| 亚洲人成色777777精品| 亚洲 无码 在线 专区| 亚洲性日韩精品一区二区三区 | 亚洲人成人77777网站| 无码专区—VA亚洲V天堂| 亚洲国产日韩在线成人蜜芽 | 亚洲综合色婷婷七月丁香| 久久亚洲国产伦理| 亚洲国产精品成人综合色在线婷婷| 久久精品国产亚洲av麻豆蜜芽 | 亚洲成色999久久网站| 亚洲三级在线播放| 亚洲aⅴ天堂av天堂无码麻豆|