我是否正确使用了状态模式?
Am I using the State Pattern correctly?
我正在学习状态模式(有限状态机)
在我构建的示例项目中,我想出更新 UI 的唯一方法是将呈现视图的引用传递给状态机,然后从状态更新 UI我在工作。我做错了吗?
这是我的状态机
class CapturePhotoStateMachine {
var noPictureTakenState: NoPictureTakenState?
var pictureTakenState: PictureTakenState?
var initialState: InitialState?
var vc: SignupAvatarView?
var capturePhotoState: CapturePhotoState?
init(viewController: SignupAvatarView) {
noPictureTakenState = NoPictureTakenState(stateMachine: self)
pictureTakenState = PictureTakenState(stateMachine: self)
initialState = InitialState(stateMachine: self)
vc = viewController
capturePhotoState = initialState
}
func setCapturePhotoState(newState: CapturePhotoState) {
self.capturePhotoState = newState
}
func takePicture() {
self.capturePhotoState?.takePicture()
}
func savePicture(image: UIImage) {
self.capturePhotoState?.savePicture(image: image)
}
func retakePicture() {
self.capturePhotoState?.retakePicture()
}
func setup() {
self.capturePhotoState?.setup()
}
}
这是我的协议
protocol CapturePhotoState {
func takePicture()
func savePicture(image: UIImage)
func retakePicture()
func setup()
}
这是状态的子类
class NoPictureTakenState: CapturePhotoState {
var stateMachine: CapturePhotoStateMachine?
init(stateMachine: CapturePhotoStateMachine) {
self.stateMachine = stateMachine
}
func takePicture() {
stateMachine!.vc?.previewView.isHidden = true
stateMachine!.vc?.capturedImage.isHidden = false
stateMachine!.vc?.saveButton.isHidden = false
stateMachine!.vc?.retakePhoto.isHidden = false
stateMachine?.setCapturePhotoState(newState: (stateMachine?.pictureTakenState)!)
}
func savePicture(image: UIImage) {
}
func retakePicture() {}
func setup() {}
}
状态机用途的关键似乎是您拥有要根据状态启用或禁用的接口对象。启用/禁用应该是视图控制器的工作。状态本身只是可以回答 "What is the current situation" 和 "What should happen next".
等问题的基础
这里有一个简短的状态机示例来说明。这是故意的琐碎。我们只有两个按钮,只有两个状态;在每种状态下,应该只启用一个按钮。状态由枚举的情况表示,我们在该枚举上使用 setter 观察器以在状态发生变化时做出响应。枚举封装了有多少个状态,下一个状态是什么的逻辑,而视图控制器则在状态变化和界面变化之间进行中介:
class ViewController: UIViewController {
@IBOutlet weak var takePictureButton: UIButton!
@IBOutlet weak var deletePictureButton: UIButton!
@IBOutlet weak var pictureImageView: UIImageView! // not used in the example
@IBAction func doTakePicture(_ sender: Any) {
// do stuff
doNextState()
}
@IBAction func doDeletePicture(_ sender: Any) {
// do stuff
doNextState()
}
enum State {
case pictureNotTaken
case pictureTaken
var nextState : State {
switch self {
case .pictureNotTaken:
return .pictureTaken
case .pictureTaken:
return .pictureNotTaken
}
}
}
var state : State = .pictureNotTaken {
didSet {
updateInterface()
}
}
func doNextState() {
self.state = self.state.nextState // triggers the setter observer
}
func updateInterface() {
switch state {
case .pictureNotTaken:
takePictureButton.isEnabled = true
deletePictureButton.isEnabled = false
case .pictureTaken:
takePictureButton.isEnabled = false
deletePictureButton.isEnabled = true
}
}
}
可能您想要的是对该模式进行一些扩展。
the only way that I figured out to update the UI is to pass a reference of the presenting view to the state machine
这就是上述模式所做的而不是所做的。 setter 观察者为我们解决了这个问题。
现在,您可能会反对 updateInterface
中的 switch
语句做了错误的工作。它将界面如何反映状态的知识完全放在视图控制器中。你的冲动是说知识肯定是 状态的一部分 (这就是你按照你的方式构建代码的原因)。
我的回答是:好吧,是的,也不是。有时我确实有这种感觉,我解决问题的方法是赋予状态机属性,表达视图控制器可能对当前状态意味着的所有问题界面。这样一来,知识就被移到了状态,但是接口仍然正确地由视图控制器管理。
因此,例如,我们可以将这两个属性添加到我们的 State 枚举中:
enum State {
// ... everything else is as before ...
var userCanTakePicture : Bool { return self == .pictureNotTaken }
var userCanDeletePicture : Bool { return self == .pictureTaken }
}
所以,现在,我们的 updateInterface
不需要任何关于每个状态含义的特殊知识;它只是询问状态接口应该是什么,这更简单并且可能给出更令人满意的权力分离:
func updateInterface() {
self.takePictureButton.isEnabled = state.userCanTakePicture
self.deletePictureButton.isEnabled = state.userCanDeletePicture
}
我正在学习状态模式(有限状态机) 在我构建的示例项目中,我想出更新 UI 的唯一方法是将呈现视图的引用传递给状态机,然后从状态更新 UI我在工作。我做错了吗?
这是我的状态机
class CapturePhotoStateMachine {
var noPictureTakenState: NoPictureTakenState?
var pictureTakenState: PictureTakenState?
var initialState: InitialState?
var vc: SignupAvatarView?
var capturePhotoState: CapturePhotoState?
init(viewController: SignupAvatarView) {
noPictureTakenState = NoPictureTakenState(stateMachine: self)
pictureTakenState = PictureTakenState(stateMachine: self)
initialState = InitialState(stateMachine: self)
vc = viewController
capturePhotoState = initialState
}
func setCapturePhotoState(newState: CapturePhotoState) {
self.capturePhotoState = newState
}
func takePicture() {
self.capturePhotoState?.takePicture()
}
func savePicture(image: UIImage) {
self.capturePhotoState?.savePicture(image: image)
}
func retakePicture() {
self.capturePhotoState?.retakePicture()
}
func setup() {
self.capturePhotoState?.setup()
}
}
这是我的协议
protocol CapturePhotoState {
func takePicture()
func savePicture(image: UIImage)
func retakePicture()
func setup()
}
这是状态的子类
class NoPictureTakenState: CapturePhotoState {
var stateMachine: CapturePhotoStateMachine?
init(stateMachine: CapturePhotoStateMachine) {
self.stateMachine = stateMachine
}
func takePicture() {
stateMachine!.vc?.previewView.isHidden = true
stateMachine!.vc?.capturedImage.isHidden = false
stateMachine!.vc?.saveButton.isHidden = false
stateMachine!.vc?.retakePhoto.isHidden = false
stateMachine?.setCapturePhotoState(newState: (stateMachine?.pictureTakenState)!)
}
func savePicture(image: UIImage) {
}
func retakePicture() {}
func setup() {}
}
状态机用途的关键似乎是您拥有要根据状态启用或禁用的接口对象。启用/禁用应该是视图控制器的工作。状态本身只是可以回答 "What is the current situation" 和 "What should happen next".
等问题的基础这里有一个简短的状态机示例来说明。这是故意的琐碎。我们只有两个按钮,只有两个状态;在每种状态下,应该只启用一个按钮。状态由枚举的情况表示,我们在该枚举上使用 setter 观察器以在状态发生变化时做出响应。枚举封装了有多少个状态,下一个状态是什么的逻辑,而视图控制器则在状态变化和界面变化之间进行中介:
class ViewController: UIViewController {
@IBOutlet weak var takePictureButton: UIButton!
@IBOutlet weak var deletePictureButton: UIButton!
@IBOutlet weak var pictureImageView: UIImageView! // not used in the example
@IBAction func doTakePicture(_ sender: Any) {
// do stuff
doNextState()
}
@IBAction func doDeletePicture(_ sender: Any) {
// do stuff
doNextState()
}
enum State {
case pictureNotTaken
case pictureTaken
var nextState : State {
switch self {
case .pictureNotTaken:
return .pictureTaken
case .pictureTaken:
return .pictureNotTaken
}
}
}
var state : State = .pictureNotTaken {
didSet {
updateInterface()
}
}
func doNextState() {
self.state = self.state.nextState // triggers the setter observer
}
func updateInterface() {
switch state {
case .pictureNotTaken:
takePictureButton.isEnabled = true
deletePictureButton.isEnabled = false
case .pictureTaken:
takePictureButton.isEnabled = false
deletePictureButton.isEnabled = true
}
}
}
可能您想要的是对该模式进行一些扩展。
the only way that I figured out to update the UI is to pass a reference of the presenting view to the state machine
这就是上述模式所做的而不是所做的。 setter 观察者为我们解决了这个问题。
现在,您可能会反对 updateInterface
中的 switch
语句做了错误的工作。它将界面如何反映状态的知识完全放在视图控制器中。你的冲动是说知识肯定是 状态的一部分 (这就是你按照你的方式构建代码的原因)。
我的回答是:好吧,是的,也不是。有时我确实有这种感觉,我解决问题的方法是赋予状态机属性,表达视图控制器可能对当前状态意味着的所有问题界面。这样一来,知识就被移到了状态,但是接口仍然正确地由视图控制器管理。
因此,例如,我们可以将这两个属性添加到我们的 State 枚举中:
enum State {
// ... everything else is as before ...
var userCanTakePicture : Bool { return self == .pictureNotTaken }
var userCanDeletePicture : Bool { return self == .pictureTaken }
}
所以,现在,我们的 updateInterface
不需要任何关于每个状态含义的特殊知识;它只是询问状态接口应该是什么,这更简单并且可能给出更令人满意的权力分离:
func updateInterface() {
self.takePictureButton.isEnabled = state.userCanTakePicture
self.deletePictureButton.isEnabled = state.userCanDeletePicture
}