Правильно ли я использую шаблон состояний? - PullRequest
0 голосов
/ 29 ноября 2018

Я изучаю шаблон состояний (конечный автомат). В примере проекта, который я построил, единственный способ, с помощью которого я решил обновить пользовательский интерфейс, - передать ссылку на представление представления на конечный автомат, а затем обновитьпользовательский интерфейс из государства я работаю.Я делаю это неправильно? 100

Вот мой конечный автомат

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() {}
}

1 Ответ

0 голосов
/ 29 ноября 2018

Ключом к цели вашего конечного автомата является то, что у вас есть объекты интерфейса, которые вы хотите включить или отключить в зависимости от состояния.Это включение / отключение должно быть задачей контроллера представления.Само состояние является просто основой для ответов на такие вопросы, как «Какова текущая ситуация» и «Что должно произойти дальше».

Вот короткий простой пример конечного автомата, который иллюстрирует.Это намеренно тривиально.У нас всего две кнопки и всего два состояния;в каждом состоянии должна быть включена ровно одна кнопка.Состояния представлены случаями перечисления, и мы используем наблюдателя-установщика для этого перечисления, чтобы отвечать при каждом изменении состояния.Перечисление инкапсулирует логику того, сколько существует состояний и каково следующее состояние, в то время как контроллер представления является посредником между изменением состояния и изменением интерфейса:

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
        }
    }
}

Вероятно, вам нужно некоторое расширение этого шаблона.

единственный способ, которым я решил обновить пользовательский интерфейс, - передать ссылку на представление представления конечному автомату

Это то, что делает вышеупомянутый шаблон не до.Наблюдатель-установщик решает эту проблему для нас.


Теперь вы можете возразить, что оператор switch в updateInterface выполняет неправильную работу.Он определяет, как интерфейс полностью отражает состояние в контроллере представления.Ваш импульс состоит в том, чтобы сказать, что эти знания, безусловно, являются частью состояния (и именно поэтому вы создали свой код так, как вы это сделали).

Мой ответ будет: ну, да, и нет,Иногда я так чувствую, и способ решения проблемы состоит в том, чтобы наделить конечный автомат свойствами, выражающими все вопросы, которые могут возникнуть у контроллера представления о том, что текущее состояние означает в отношении интерфейса.Таким образом, знание перемещается в состояние, но интерфейс все еще корректно управляется контроллером представления.

Так, например, мы могли быдобавьте эти два свойства в наше перечисление 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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...