Реализация государственного образца - PullRequest
0 голосов
/ 15 мая 2019

Я должен реализовать конечный автомат с очень базовым требованием, чтобы каждый шаблон состояния имел:

  1. Конечный автомат может находиться одновременно в ОДНОМ состоянии.
  2. Переход из состояния X в состояние Y будет иметь другие параметры, чем переход из состояния Y в Z или из любого другого состояния в другое.
  3. Пользовательская программа, которая будет управлять «конечным автоматом», не может, конечно, перейти в состояние, в которое нельзя перейти, если вы находитесь в определенном состоянии. например, stateMachine.dispenseCard() не будет работать, если stateMachine.currentState() не CASHACCEPTED

Я пытался перейти по этой ссылке, но здесь:

  1. Абстрактный класс State должен определять все возможные состояния конечного автомата, поэтому конкретное состояние должно реализовывать все методы состояния. Почему конкретный государственный класс должен интересоваться всеми другими методами, которые переходят в другие государства? Почему не только те, в которые переходит это состояние?

    public abstract class DoorState : DomainObject    {
    protected Door _door;
    public Door Door
    {
        get { return _door; }
        set { _door = value; }
    }
    public abstract void Close();
    public abstract void Open();
    public abstract void Break();
    public abstract void Lock();
    public abstract void Unlock();
    /// <summary>
    /// Fix simulates a repair to the Door and resets 
    /// the initial state of the door to closed.
    /// </summary>
    public void Fix()
    {
        _door.DoorState = new DoorClosedState(this);
    }}
    
  2. Почему в классе State есть «Устройство», которое переходит в разные состояния? Не должно ли быть наоборот? Как Дверь должна «иметь» состояние.

1 Ответ

0 голосов
/ 15 мая 2019

Пример кода, который вы дали, на самом деле определяет Состояние , которое имеет все Поведения или Context (Дверь в этом примере). State определяет, как Context должен вести себя в этом состоянии.

Например, когда Door находится в DoorOpenedState (предположим, что он полностью открыт).когда вызывается метод Open() для вызова поведения открытия Door, это должно привести к ошибке (недопустимый переход), поскольку вы не можете перейти с DoorOpenedState на DoorOpenedState

Состояниешаблоны могут быть реализованы многими различными способами, и переход между состояниями может быть достигнут различными способами.Если вы не читали книгу GOF , там обсуждаются вопросы перехода и возможные реализации.

Вот пример конечного автомата для торгового автомата.Чтобы упростить пример и сконцентрироваться на конечном автомате и переходах, скажем, что у нашего конечного автомата есть только лапша и он не возвращает лишних денег.Поэтому, если чашка лапши стоит 5 $, а вы даете ей 7 $, она не вернет 2 $.

ПРИМЕЧАНИЕ. Поскольку требуется обмен данными между NoodleVendingMachine и каждым состоянием. Для простоты яЯ сделаю эти методы внутренними, чтобы просто пометить их.Для реального проекта может потребоваться дополнительный интерфейс, чтобы скрыть их от клиентского кода NoodleVendingMachine и сохранить их между NoodleVendingMachine и только его состояниями.

public class CacheStorage {

    public Cache AvailableCache { get; private set; }

    public void AddCache(Money cache) {
        AvailabletCache += cache;
    }

    public void ClearAvailableCache() {
        AvailabletCache = Money.None;
    }
}

public interface INoodleVendingMachineState {

    void TakeCache(Money money);

    Noodles DispenceNoodles();

    Money ReturnCache();
}

public class NoodleVendingMachine {

    private INoodleVendingMachineState mState;

    itnernal CacheStorage CacheStorage { get; private set; }

    public NoodlesPrice { get; private set; }

    public Money AvailableCache { get { return CacheStorage.AvailableCache; } }

    public NoodleVendingMachine() {

        NoodlesPrice = new Money(Currency.USD, 5); // 5 bucks for noodles
        CacheStorage = new CacheStorage();
        mState = new WaitingForCacheState(this);
    }

    public void TakeCache(Money money) {
        mState.TakeCache(money);
    }

    public Noodles DispenceNoodles() {
        return mState.DispenceNoodles();
    }

    public Money ReturnCache() {
        return mState.ReturnCache();
    }

    internal void TransitionTo(INoodleVendingMachineState state) {
        mState = state;
    }
}

public WaitingForCacheState : INoodleVendingMachineState {

    private NoodlesVendingMachine mVendingMachine;

    public WaitingForCacheState(NoodlesVendingMachine vendingMachine) {
        mVendingMachine = vendingMachine;
    }

    public void TakeCache(Money cache) { 

        mVendingMachine.CacheStorage.AddCache(cache);
        mVendingMachine.TransitionTo(new CacheAvailableState(mVendingMachine));
    }

    public Noodles DispenceNoodles() { 
        throw new InsertCacheFirstException();
    }

    public Money ReturnCache() {
        throw new CacheNotAvailableException();
    }
}

public CacheAvailableState : INoodleVendingMachineState {

    private CacheStorage mCacheStorage;
    private NoodleVendingMachine mVendingMachine;

    public CacheAvailableState(NoodleVendingMachine vendingMachine) {

        if (vendingMachine.AvailableCache == Money.None){
            throw new CacheNotAvailable()
        }

        mVendingMachine = vendingMachine;
        mCacheStorage = mVendingMachine.CacheStorage;
    }

    public void TakeCache(Money cache) {
         mCacheStorage.AddCache(cache);
    }

    public Noodles DispenceNoodles() {

        if(mCacheStorage.AvailableCache < mVendingMachine.NoodlesPrice) {
            throw new CacheNotEnoughtException();
        }

        mCacheStorage.ClearAvailableCache();

        mVendingMachine.TransitionTo(new WaitingForCacheState(mVendingMachine));

        return new Noodles();
    }

    public Money ReturnCache() {
        var cache = mCacheStorage.AvailableCache;
        mCacheStorage.ClearAvailableCache();
        mVendingMachine.TransitionTo(new WaitingForCacheState(mVendingMachine));
        return cache;
    }
}

Здесь мы фиксируем поведение торгового автомата с состояниями .

WaitingForCacheState будет выдавать исключения при вызове DispenceNoodles или ReturnCache, так как это некорректное поведение в этом состоянии.

WaitingForCacheState выполнит переход состояния в CacheAvailableState, когдапользовательский ввод кеша.Когда кэш доступен, мы видим, что все поведения поддерживаются.Когда лапша выдается или пользователь просит вернуть свои деньги, мы делаем переход состояния в WaitingForCacheState.

В этом примере каждое состояние выполняет переходы состояния в следующее соответствующее состояние.

Если выЕсли у вас есть более сложный пример для вашего конечного автомата, вам, вероятно, потребуется решить, где хранить параметры.Вы можете сохранить его в Context (NoodlesVendingMachine в нашем случае).В этом примере деньги хранятся в специальном CacheStorage, так что каждый штат и NoodlesVendingMachine имеют доступ к ним, и они могут принимать решения на основе их стоимости.когда действие выполняется (например, DispenceNoodles), текущее состояние проверяет значение CacheStorage и принимает решение, выполнить ли переход, выполнить какое-либо поведение (TakeMoney in CacheAvailableState), сгенерировать ошибку иливыполнить поведение и затем выполнить переход (ReturnCache в CacheAvailableState).

Конечно, если необходимо, вы можете хранить временные данные, специфичные для состояния, в каждом State, чтобы он мог принимать решения на основе этих данных бездругие объекты знают об этом.

В этом примере CacheAvailableState может хранить AvailableCache в нем как свойство.Я решил добавить его в другой класс, чтобы показать, что таким образом несколько объектов могут иметь доступ к данным.Конечно, нам нужно показать AvailableCache пользователю, поэтому NoodlesVendingMachine также необходим доступ к доступному кешу.

Мы также можем добавить его в NoodlesVendingMachine, но это добавит методы и свойства в класс и увеличит его размер.Поэтому мы используем принцип единой ответственности и переносим ответственность за хранение кеша на другой класс.Это будет особенно эффективно, если у нас будет больше данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...