.Net: Это неуклюжий или умный способ управления состояниями приложений?(Остановился | Бег | Приостановлено | StopPending | StartPending | PausePending | ContinuePending) - PullRequest
0 голосов
/ 01 ноября 2010

Я разрабатываю приложение Windows Service некоторой сложности, и мне нужен способ постепенного перехода между состояниями.Поскольку каждое состояние может нуждаться в инициализации / очистке, должен существовать механизм, который координирует переходы между ними и гарантирует, что из любого данного состояния могут быть достигнуты только допустимые состояния.Указанные состояния являются знакомыми состояниями службы Windows:

Stopped|Running|Paused|StopPending|StartPending|PausePending|ContinuePending

Я разработал систему, которая кажется , чтобы сделать это правильно, но мне любопытноесть ли у него недостатки, которые я просто еще не наткнулся, и / или есть ли более проверенные и проверенные шаблоны / лучшие практики, которые я мог бы или должен использовать вместо этого.

Это реализовано в классе Bootstrapperкоторый предоставляет методы, соответствующие командам, которые я должен ожидать от службы Windows: Start, Stop, Pause, Continue (чтобы команда OnStart службы просто вызывала метод Start класса, OnStop будет вызывать Stop и т. д.):

В служебном коде (это VB.Net, но мне пришлось использовать здесь // для комментариев, поскольку в листинге кода, казалось, что-то не так):

Sub OnStart()
    Bootstrapper.Start()
End Sub

В Start()метод моего класса (здесь я пропускаю потоки и обработку исключений и другую логику для простоты):

Public Sub Start()
    If RequestState(State.Running) = True Then
        // Log success
    Else
        // Log failure
    End If
End Sub

Тогда внутри моего класса есть аналогичный набор приватных OnStart(), OnStop(), OnPause() и OnContinue() методы, которые выполняют фактическую инициализацию / очистку для каждого состояния:

Private Function OnStart() As Boolean
    SetState(State.StartPending)
        // Do something
    Return SetState(State.Running)
End Function

Как вы можете видеть выше, есть вызовы для двух других методов - RequestState() иSetState().Вот где на самом деле имеет место логика, и она работает следующим образом:

Команда отправляется приложению (Start, Stop, Pause или Continue).Получающий метод вызывает RequestState() с желаемым конечным состоянием, передаваемым в качестве параметра.Если это состояние было достижимым, оно возвращает True, в противном случае False.

RequestState() будет использовать конструкции Select для определения правильного действия на основе текущего состояния и запрошенного состояния.

SetState() фактически устанавливает состояние приложения и после этого проверяет, есть ли какое-то «следующее» состояние «в очереди» (это так, чтобы процесс StartPending мог завершиться и закончиться вСостояние выполнения, до того, как будет введена команда Пауза.)

Простите, если я представлю эти методы в беспорядочном порядке, но с того места, где я сижу, показалось, что это правильный способ представить функциональность.Вот методы RequestState() и SetState() (извините за большую длину, но я чувствовал, что это было необходимо для полноты):

Private _currentState As State
Private _nextState As State

Private Function RequestState(ByVal requestedState As State) As Boolean
    Select Case requestedState
        Case State.StartPending, State.StopPending, State.PausePending, State.ContinuePending, State.Exception, State.None
            Throw New ArgumentException(requestedState.ToString & " cannot be requested directly.")
        Case Else

            _nextState = requestedState

            Select Case _currentState
                Case State.Exception
                    Return False

                Case State.Stopped
                    Select Case requestedState
                        Case State.Stopped
                            Return True
                        Case State.Running
                            Return OnStart()
                        Case Else
                            Return False
                    End Select

                Case State.Running
                    Select Case requestedState
                        Case State.Stopped
                            Return OnStop()
                        Case State.Running
                            Return True
                        Case State.Paused
                            Return OnPause()
                        Case Else
                            Return False
                    End Select

                Case State.Paused
                    Select Case requestedState
                        Case State.Stopped
                            Return OnStop()
                        Case State.Running
                            Return OnContinue()
                        Case State.Paused
                            Return True
                        Case Else
                            Return False
                    End Select

                Case State.StartPending
                    Select Case requestedState
                        Case State.Stopped
                            _nextState = State.Stopped
                            Return True
                        Case State.Running
                            Return True
                        Case State.Paused
                            _nextState = State.Paused
                            Return True
                        Case Else
                            Return False
                    End Select

                Case State.StopPending
                    Select Case requestedState
                        Case State.Stopped
                            Return True
                        Case State.Running
                            _nextState = State.Running
                            Return True
                        Case Else
                            Return False
                    End Select

                Case State.ContinuePending
                    Select Case requestedState
                        Case State.Stopped
                            _nextState = State.Stopped
                            Return True
                        Case State.Running
                            Return True
                        Case State.Paused
                            _nextState = State.Paused
                            Return True
                        Case Else
                            Return False
                End Select

                Case State.PausePending
                    Select Case requestedState
                        Case State.Stopped
                            _nextState = State.Stopped
                            Return True
                        Case State.Running
                            _nextState = State.Running
                            Return True
                        Case State.Paused
                            Return True
                        Case Else
                            Return False
                    End Select

                Case Else
                    Return False

            End Select
    End Select
End Function

Private Function SetState(ByVal newState As State) As Boolean
    _currentState = newState
    If newState = State.Running OrElse newState = State.Stopped OrElse newState = State.Paused Then
        If _currentState = _nextState Then
            Return True
        End If
        Return RequestState(_nextState)
    Else
        Return True
    End If
End Function

1 Ответ

1 голос
/ 01 ноября 2010

Я бы сказал, что все в порядке, как есть. Там не так много государств, и за ним достаточно легко следовать. Я бы сказал, что есть места, где вы можете сделать код немного короче, но в остальном это не так уж плохо.

Однако, может быть проще создать таблицу реальных состояний. Начните с отображения вашей таблицы состояний в виде таблицы:

Start State        Requested State      State Changed?
-----------        ---------------      --------------
Exception          { any }              False
Stopped            Running              OnStart()
Paused             Stopped              OnStop()
Paused             Running              OnPause()
StartPending       Running              True
StartPending       Paused               True
etc

Теперь, чтобы преобразовать это в код, учтите, что это в основном Dictionary<State, Dictionary<State, Func<bool>>>, где внешний ключ - это StartState, а внешнее значение - ваша таблица перехода.

Этот код не проверен , но он должен дать вам общее представление о том, с чего начать (используя C #, поскольку мой VB-fu ржавый):

public bool RequestState(State requestedState)
{
    _nextState = requestedState;
    if (requestedState == currentState)
        return true;

    var stateTransitions = new Dictionary<State, Dictionary<State, Func<bool>>>
    {
        { State.Exception, new Dictionary<State, Func<bool>(),
        { State.Stopped, new Dictionary<State, Func<bool>>
            {
                { State.Running, () => OnStart() }
            }
        },
        { State.Paused, new Dictionary<State, Func<bool>>
            {
                { State.Stopped, () => OnStop() },
                { State.Running, () => OnPause() },
            }
        },
        { State.StartPending, new Dictionary<State, Func<bool>>
            {
                { State.Running, () => true },
                { State.Paused, () => true },
            }
        },
        // remaining states
    };

    var transition = stateTransitions[currentState];
    Func<bool> transitionAction;
    if (transition.TryGetValue(requestedState, out transitionAction))
        return transitionAction();
    return false;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...