Как сохранить объекты, реализующие шаблон состояния?

Я новичок в шаблоне проектирования штата, и я не могу найти подходящий пример сохранения различных состояний объекта в базе данных (SQL Server в моем случае). Сценарий довольно похож [почти идентичный] на пример, описанный в следующей статье, однако я не нашел применимого решения для сохранения состояний в базе данных. Можете ли вы, ребята, порекомендовать ссылку или, возможно, привести пример?

Использование шаблона состояния и образец в С#

Кроме того: как вы перечисляете все типы ConcreteState во время выполнения? Например, если у вас есть 10 разных состояний, объявляете ли вы EnumStates с 10 различными членами и предоставляете каждому члену ConcreteState ассоциированный член EnumStates или получаете все отдельные состояния, получая подклассы ConcreteState?

Для получения информации, мне нужно иметь возможность искать объекты на основе их разных состояний.

Ответ 1

Экземпляры государств не имеют самих себя, поэтому все, что вам нужно сохранить, - это каждая государственная идентичность. Не рекомендуется сохранять имя класса State в базе данных, потому что база данных должна измениться, если вы измените имя класса State. Вместо

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

Чтобы вернуть состояние при загрузке объекта,

  • мгновенно создать объект-член объекта и присвоить его члену объекта объекта или
  • если это дорого для создания экземпляра состояния, изменить объект для доступа к члену государства с помощью метода и лениво создать экземпляр состояния в этом методе на основе значения члена перечисления идентификатора состояния.

В любом случае вы должны иметь возможность перейти от значения Enum к состоянию. Сделайте это, перейдя через все соответствующие классы состояний, пока не найдете тот, чье значение идентичности соответствует.

Итак, каковы соответствующие государства? Это зависит от того, кто пишет государственные классы.

  • В простом случае, когда вы контролируете всю программу, и все классы состояний в программе потенциально являются членами объекта, имеющего статус, вы можете просто перебрать все подклассы или исполнители государственного суперкласса или интерфейс, например: Получение всех типов, реализующих интерфейс.

  • Если по какой-то причине существуют классы состояний, которые вы не хотите перебирать, просто определите список тех, которые вы хотите перебрать в константу, или (если вы хотите изменить его без изменение кода) в конфигурации.

Если ваш список классов состояний выполняется медленно, просто выполните его один раз при запуске или первом запуске программы. Если вы жестко указали этот список, не делайте этого в классе State-having (он должен быть независим от конкретных государств) или в государственном суперклассе (который вводит циклическую зависимость); поместите список выше (зависимый) в вашу программу или (как предложил Фархад) в своем классе.

Существует множество примеров того, как сохранять объекты с помощью состояния там; этот является относительно простым.

Ответ 2

Не пытайтесь перевести состояния в столбцы в таблице, которые не будут работать.

Вместо сериализации состояний с использованием JSON.NET, поскольку он поддерживает наследование. Затем сохраните его в таблице, например:

create table OrderStates
(
    OrderId int not null,
    Data nvarchar(MAX) not null
);

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

Чтобы активировать поддержку наследования в JSON.NET, вы должны использовать:

var json = JsonConvert.SerializeObject(yourState, typeof(StateBaseClass), JsonConvert.DefaultSettings)`. 
using (var cmd = sqlConnection.CreateCommand())
{
    cmd.CommandText = "INSERT INTO OrderStates (OrderId, Data) VALUES(@OrderId, @Data)";
    cmd.Parameters.AddWithValue("OrderId", orderId);
    cmd.Parameters.AddWithValue("Data", json);
    cmd.ExecuteNonQuery();
}

То же самое происходит при десериализации, укажите базовый класс при использовании JsonConvert.DeserializeObject().

Как вы перечисляете все типы ConcreteState во время выполнения? Например, если у вас есть 10 разных состояний, объявляете ли вы EnumStates с 10 различными членами и предоставляете каждому члену ConcreteState ассоциированный член EnumStates или получаете все отдельные состояния, получая подклассы ConcreteState?

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

Ответ 3

Мне не понравился приведенный ниже пример. Ниже перечислены причины:

  • Я согласен с тем, что сохранившаяся архитектура будет беспорядочной на мой взгляд.
  • Создание нового экземпляра за состояние кажется мне эффективным суицидальным шаблоном.
  • Тестирование будет ад... найти ошибки будет ад.. отладка будет ад.
  • В более чем 30-летнем опыте я никогда не видел, чтобы этот шаблон использовался даже один раз в приложении, ориентированном на данные - я видел его и использовал его в тех случаях, когда мне не нужно сохранять информацию, например, при создании сетевого уровня - на один порт можно было бы обработать такой тип состояния.


Вместо этого я бы выбрал этот шаблон:

инфраструктура шаблонов

public interface IStateObject<T>
{
    T State { get; set; }
    void Process();
}

Пример реализации для некоторого объекта псевдообъекта

public enum OrderState
{
    Taken,
    Approved,
    Payed,
    Emailed,
    BeforeShipment
    //etc.. etc..
 }

 public class Order : IStateObject<OrderStates>
 {
     //some linear fields of order..
     //: name, description, etc.. etc..

     public OrderStates State { get; set; }

     public void Process()
     {
         switch (State)
         {
             case OrderState.Taken:
                 // code to handle this state
                 break;
             case OrderState.Approved:
                 // etc..
                 break;
          }
         //persist myself to db.
     }
 }

Это очень просто, так как вы можете сохранять объект за один тип для контекста объекта в одной строке.
Также объект создается один раз, как интуитивно он должен был иметь, если бы у нас не было компьютера рядом с нами.
Но в основном потому, что он очень прямолинейный и очень гибкий.

Возможно, вы заметили, что вам вообще может не понадобиться IStateObject<T>, но я бы сказал, что вам это понадобится позже, когда вы захотите обработать вертикальные решения. имейте в виду, что T не обязательно должен быть перечислением. он может служить общей основой для развития в соответствии с потребностями вашего приложения.

Чтобы еще раз отметить беспорядок, о котором я упомянул в начале этого ответа,
Предположим, что мы хотим иметь историю с предыдущими состояниями порядка:

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

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

Посмотрите на мой вопрос? шаблон штата просто не предназначен для приложений, ориентированных на данные.

Удачи.

Ответ 4

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

Я думаю, что в этом сценарии должен быть класс StateManager, который может также содержать определение Enum состояний, а также отображение между каждым значением Enum и его объектом State (Dictionary<StateEnum, State>). Это сопоставление должно быть либо закодировано, либо считываться из файла конфигурации. Этот класс может обрабатывать ленивую загрузку состояний при первом доступе. Он также может создавать их как Singleton, если у государства действительно нет полей, а скорее функциональных возможностей (как в примере ссылки в представлении OP).