Design Patterns(二十一):State Pattern--VB代码
結構圖
?
角色
- 環境(Subject)角色:負責定義客戶感興趣的接口,并維護一個具體狀態(ConcreteState)的實例,以及維護狀態轉換或為這種轉換提供支持(因為狀態轉換有時候可能是由上層應用發起的)。
- 狀態(State)角色:定義一個接口以封裝與環境(Context)角色的一個特定狀態相關的行為。
- 具體狀態(ConcreteState)角色:)每一個子類實現一個與環境(Context)角色狀態相關的行為。
動機
在軟件構建過程中,某些對象的狀態如果改變,其行為也會隨之而發生變化,比如文檔處于只讀狀態,其支持的行為和讀寫狀態支持的行為就可能完全不同。
???? 如何在運行時根據對象的狀態來透明地更改對象的行為?而不會為對象操作和狀態轉化之間引入緊耦合?
意圖
?? 允許一個對象在其內部狀態改變時改變它的行為。從而使對象看起來似乎修改了其行為。
示意性代碼
示意性代碼'State?pattern?--?Structural?example
'MainApp?test?application
Module?MainApp
????Public?Sub?Main()
????????'Setup?context?in?a?state
????????Dim?c?As?New?Context(New?ConcreteStateA)
????????'Issue?requests,?which?toggles?state
????????c.Request()
????????c.Request()
????????c.Request()
????????c.Request()
????????'Wait?for?user?
????????Console.ReadLine()
????End?Sub
End?Module
'"State"
Public?MustInherit?Class?State
????Public?MustOverride?Sub?Handle(ByVal?context?As?Context)
End?Class
'"ConcreteStateA"
Public?Class?ConcreteStateA
????Inherits?State
????Public?Overrides?Sub?Handle(ByVal?context?As?Context)
????????context.State?=?New?ConcreteStateB
????End?Sub
End?Class
'"ConcreteStateB"
Public?Class?ConcreteStateB
????Inherits?State
????Public?Overrides?Sub?Handle(ByVal?context?As?Context)
????????context.State?=?New?ConcreteStateA
????End?Sub
End?Class
'"Context"
Public?Class?Context
????'Constructor?
????Public?Sub?New(ByVal?state?As?State)
????????Me._state?=?state
????End?Sub
????'Property
????Private?_state?As?State
????Public?Property?State()?As?State
????????Get
????????????Return?_state
????????End?Get
????????Set(ByVal?value?As?State)
????????????_state?=?value
????????End?Set
????End?Property
????Public?Sub?Request()
????????State.Handle(Me)
????End?Sub
End?Class
?
?一個實例
??? 下面的狀態模式代碼演示了一個帳戶在處于不同狀態時可以進行不同的操作。在行為上的區別委派給以下三個對象紅色狀態(RedState),灰色狀態(SilverState),金色狀態(GoldState)。這些狀態分別代表透支帳戶,新開通的帳戶,和擁有良好信譽的帳戶。
舊系統代碼'State?pattern?--?Real?World?example
'MainApp?test?application
Module?MainApp
????Public?Sub?Main()
????????'Open?a?new?account
????????Dim?account?As?New?Account("Jim?Johnson")
????????'Apply?financial?transactions
????????account.Deposit(500.0)
????????account.Deposit(300.0)
????????account.Deposit(550.0)
????????account.PayInterest()
????????account.Withdraw(2000.0)
????????account.Withdraw(1100.0)
????????'Wait?for?user
????????Console.ReadLine()
????End?Sub
End?Module
'"Context"
Public?Class?Account
????Private?owner?As?String
????Private?serviceFee?As?Double
????Private?interest?As?Double
????'Constructor
????Public?Sub?New(ByVal?owner?As?String)
????????'New?accounts?are?'Silver'?by?default
????????Me.owner?=?owner
????????_state?=?"SilverState"
????End?Sub
????'Properties
????Protected?_balance?As?Double
????Public?ReadOnly?Property?Balance()?As?Double
????????Get
????????????Return?_balance
????????End?Get
????End?Property
????Private?_state?As?String
????Public?Property?State()?As?String
????????Get
????????????Return?_state
????????End?Get
????????Set(ByVal?value?As?String)
????????????_state?=?value
????????End?Set
????End?Property
????Public?Sub?Deposit(ByVal?amount?As?Double)
????????_balance?+=?amount
????????StateChangeCheck()
????????Console.WriteLine("Deposited?{0:C}?---?",?amount)
????????Console.WriteLine("?Balance?=?{0:C}",?Me.Balance)
????????Console.WriteLine("?Status?=?{0}",?_
?????????_state)
????????Console.WriteLine("")
????End?Sub
????Public?Sub?Withdraw(ByVal?amount?As?Double)
????????If?_state?=?"RedState"?Then
????????????serviceFee?=?15.0
????????????amount?-=?serviceFee
????????????Console.WriteLine("No?funds?available?for?withdrawal!")
????????Else
????????????_balance?-=?amount
????????End?If
????????StateChangeCheck()
????????Console.WriteLine("Withdrew?{0:C}?---?",?amount)
????????Console.WriteLine("?Balance?=?{0:C}",?Me.Balance)
????????Console.WriteLine("?Status?=?{0}",?_
?????????_state)
????????Console.WriteLine("")
????End?Sub
????Public?Sub?PayInterest()
????????Select?Case?_state
????????????Case?"RedState"
????????????????'No?Interest?is?paid
????????????Case?"SilverState"
????????????????interest?=?0.0
????????????????_balance?+=?interest?*?_balance
????????????Case?"GoldState"
????????????????interest?=?0.05
????????????????_balance?+=?interest?*?_balance
????????End?Select
????????Console.WriteLine("Interest?Paid?---?")
????????Console.WriteLine("?Balance?=?{0:C}",?Me.Balance)
????????Console.WriteLine("?Status?=?{0}",?_
?????????_state)
????????Console.WriteLine("")
????End?Sub
????Private?Sub?StateChangeCheck()
????????If?Balance?<?0.0?Then
????????????_state?=?"RedState"
????????ElseIf?_balance?<?1000?Then
????????????_state?=?"SilverState"
????????Else
????????????_state?=?"GoldState"
????????End?If
????End?Sub
End?Class
在開發中時常遇到的根據不同的狀態需要進行不同的處理操作的問題,而這樣的問題,大部分人是采用Select-Case/If-Else語句進行處理的,這樣會造成一個問題:分支過多,而且如果加入一個新的狀態就需要對原來的代碼進行編譯.State模式采用了對這些不同的狀態進行封裝的方式處理這類問題,當狀態改變的時候進行處理然后再切換到另一種狀態,也就是說把狀態的切換責任交給了具體的狀態類去負責.具體的狀態類可以通過派生的方式不依賴于其他對象而獨立變化。
運用模式后的代碼'State?pattern?--?Real?World?example
'MainApp?test?application
Module?MainApp
????Public?Sub?Main()
????????'Open?a?new?account
????????Dim?account?As?New?Account("Jim?Johnson")
????????'Apply?financial?transactions
????????account.Deposit(500.0)
????????account.Deposit(300.0)
????????account.Deposit(550.0)
????????account.PayInterest()
????????account.Withdraw(2000.0)
????????account.Withdraw(1100.0)
????????'Wait?for?user
????????Console.ReadLine()
????End?Sub
End?Module
'"State"
Public?MustInherit?Class?State
????'Fields
????Protected?interest?As?Double
????Protected?lowerLimit?As?Double
????Protected?upperLimit?As?Double
????'Properties
????Protected?_account?As?Account
????Public?Property?Account()?As?Account
????????Get
????????????Return?_account
????????End?Get
????????Set(ByVal?value?As?Account)
????????????_account?=?value
????????End?Set
????End?Property
????Protected?_balance?As?Double
????Public?Property?Balance()?As?Double
????????Get
????????????Return?_balance
????????End?Get
????????Set(ByVal?value?As?Double)
????????????_balance?=?value
????????End?Set
????End?Property
????Public?MustOverride?Sub?Deposit(ByVal?amount?As?Double)
????Public?MustOverride?Sub?Withdraw(ByVal?amount?As?Double)
????Public?MustOverride?Sub?PayInterest()
End?Class
'"ConcreteState"
'Account?is?overdrawn
Public?Class?RedState
????Inherits?State
????Private?serviceFee?As?Double
????'Constructor
????Public?Sub?New(ByVal?state?As?State)
????????Me.Balance?=?state.Balance
????????Me.Account?=?state.Account
????????Initialize()
????End?Sub
????Private?Sub?Initialize()
????????'Should?come?from?a?datasource
????????interest?=?0.0
????????lowerLimit?=?-100.0
????????upperLimit?=?0.0
????????serviceFee?=?15.0
????End?Sub
????Public?Overrides?Sub?Deposit(ByVal?amount?As?Double)
????????Balance?+=?amount
????????StateChangeCheck()
????End?Sub
????Public?Overrides?Sub?Withdraw(ByVal?amount?As?Double)
????????amount?-=?serviceFee
????????Console.WriteLine("No?funds?available?for?withdrawal!")
????End?Sub
????Public?Overrides?Sub?PayInterest()
????????'No?intereset?is?paid
????End?Sub
????Private?Sub?StateChangeCheck()
????????If?Balance?>?upperLimit?Then
????????????Account.State?=?New?SilverState(Me)
????????End?If
????End?Sub
End?Class
'"ConcreteState"
'Silver?is?non-interest?bearing?state
Public?Class?SilverState
????Inherits?State
????'Overloaded?constructors
????Public?Sub?New(ByVal?state?As?State)
????????Me.new(state.Balance,?state.Account)
????End?Sub
????Public?Sub?New(ByVal?balance?As?Double,?ByVal?account?As?Account)
????????Me.Balance?=?balance
????????Me.Account?=?account
????????Initialize()
????End?Sub
????Private?Sub?Initialize()
????????'Should?come?from?a?datasource
????????interest?=?0.0
????????lowerLimit?=?0.0
????????upperLimit?=?1000.0
????End?Sub
????Public?Overrides?Sub?Deposit(ByVal?amount?As?Double)
????????Balance?+=?amount
????????StateChangeCheck()
????End?Sub
????Public?Overrides?Sub?Withdraw(ByVal?amount?As?Double)
????????Balance?-=?amount
????????StateChangeCheck()
????End?Sub
????Public?Overrides?Sub?PayInterest()
????????Balance?+=?interest?*?Balance
????????StateChangeCheck()
????End?Sub
????Private?Sub?StateChangeCheck()
????????If?Balance?<?lowerLimit?Then
????????????Account.State?=?New?RedState(Me)
????????ElseIf?Balance?>?upperLimit?Then
????????????Account.State?=?New?GoldState(Me)
????????End?If
????End?Sub
End?Class
'"ConcreteState"
'Interest?bearing?state
Public?Class?GoldState
????Inherits?State
????'Overloaded?constructors
????Public?Sub?New(ByVal?state?As?State)
????????Me.new(state.Balance,?state.Account)
????End?Sub
????Public?Sub?New(ByVal?balance?As?Double,?ByVal?account?As?Account)
????????Me.Balance?=?balance
????????Me.Account?=?account
????????Initialize()
????End?Sub
????Private?Sub?Initialize()
????????'Should?come?from?a?datasource
????????interest?=?0.05
????????lowerLimit?=?1000.0
????????upperLimit?=?10000000.0
????End?Sub
????Public?Overrides?Sub?Deposit(ByVal?amount?As?Double)
????????Balance?+=?amount
????????StateChangeCheck()
????End?Sub
????Public?Overrides?Sub?Withdraw(ByVal?amount?As?Double)
????????Balance?-=?amount
????????StateChangeCheck()
????End?Sub
????Public?Overrides?Sub?PayInterest()
????????Balance?+=?interest?*?Balance
????????StateChangeCheck()
????End?Sub
????Private?Sub?StateChangeCheck()
????????If?Balance?<?0.0?Then
????????????Account.State?=?New?RedState(Me)
????????ElseIf?Balance?<?lowerLimit?Then
????????????Account.State?=?New?SilverState(Me)
????????End?If
????End?Sub
End?Class
'"Context"
Public?Class?Account
????Private?owner?As?String
????'Constructor
????Public?Sub?New(ByVal?owner?As?String)
????????'New?accoounts?are?'Silver'?by?default
????????Me.owner?=?owner
????????_state?=?New?SilverState(0.0,?Me)
????End?Sub
????'Properties
????Public?ReadOnly?Property?Balance()?As?Double
????????Get
????????????Return?_state.Balance
????????End?Get
????End?Property
????Private?_state?As?State
????Public?Property?State()?As?State
????????Get
????????????Return?_state
????????End?Get
????????Set(ByVal?value?As?State)
????????????_state?=?value
????????End?Set
????End?Property
????Public?Sub?Deposit(ByVal?amount?As?Double)
????????State.Deposit(amount)
????????Console.WriteLine("Deposited?{0:C}?---?",?amount)
????????Console.WriteLine("?Balance?=?{0:C}",?Me.Balance)
????????Console.WriteLine("?Status?=?{0}",?_
?????????_state.GetType().Name)
????????Console.WriteLine("")
????End?Sub
????Public?Sub?Withdraw(ByVal?amount?As?Double)
????????State.Withdraw(amount)
????????Console.WriteLine("Withdrew?{0:C}?---?",?amount)
????????Console.WriteLine("?Balance?=?{0:C}",?Me.Balance)
????????Console.WriteLine("?Status?=?{0}",?_
?????????_state.GetType().Name)
????????Console.WriteLine("")
????End?Sub
????Public?Sub?PayInterest()
????????_state.PayInterest()
????????Console.WriteLine("Interest?Paid?---?")
????????Console.WriteLine("?Balance?=?{0:C}",?Me.Balance)
????????Console.WriteLine("?Status?=?{0}",?_
?????????Me.State.GetType().Name)
????????Console.WriteLine("")
????End?Sub
End?Class
?
State?Pattern模式的幾個要點:
1、State模式將所有與一個特定狀態相關的行為都放入一個State的子類對象中,在對象狀態切換時,切換相應的對象;但同時維持State的接口,這樣實現了具體操作與狀態轉換之間的解耦。
2、為不同的狀態引入不同的對象使得狀態轉換變得更加明確,而且可以保證不會出現狀態不一致的情況,因為轉換是原子性的——即要么徹底轉換過來,要么不轉換。
?????3、如果State對象沒有實例變量,那么各個上下文可以共享一個State對象,從而節省對象開銷。
我的理解
封裝與狀態相關的行為,支持狀態的變化。
參考資料
《C#面向對象設計模式縱橫談系列課程(21)》?????李建中老師
總結
以上是生活随笔為你收集整理的Design Patterns(二十一):State Pattern--VB代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 走向成功 开发自我
- 下一篇: 冯小刚导演系列公益短片之林心如版