Государственный шаблон дизайна с полиморфными объектами - PullRequest
1 голос
/ 27 октября 2011

У меня есть иерархия объектов, которые будут иметь одинаковое поведение.Я хочу отделить поведение от определений POCO.Поскольку поведение представляет собой перемещение объектов в различные состояния, мне кажется, что это работа для шаблона состояний.Но это не так просто, как наличие отдельного определения для каждой функции, поскольку каждый объект может иметь немного различное поведение.

Например, допустим, у меня есть следующие классы, основанные на абстрактном базовом классе:

public abstract BaseClass
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
}

public Class1 : BaseClass
{
    public string PropertyA { get; set; }
    public string PropertyB { get; set; }
}

public Class2 : BaseClass
{
    public string PropertyC { get; set; }
    public string PropertyD { get; set; }
}

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

Теперь поведение каждой функции также является иерархическим, то есть вызов функции Approve () для Class1 должен бытьтак же, как вызов унаследованного поведения из BaseClass, но Class2 переопределяет функцию Approve (), поскольку этот тип следует за другим процессом утверждения.

Я теряюсь, пытаясь применить шаблон состояния к этим объектам.Я мог бы поставить функции на сами объекты и наследовать их таким образом, и это прекрасно работает, но это нарушает дизайн POCO.Я также мог бы реализовать функцию Approve () с оператором switch для каждого типа объекта, но это нарушает мой полиморфный дизайн.

Как я могу применить шаблон состояния к определению многослойного полиморфного объекта и оставаться в соответствии спринципы проектирования.

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

Ответы [ 3 ]

1 голос
/ 27 октября 2011

Размещение методов на объекте не нарушает «дизайн POCO» (стандартного «дизайна POCO» не существует), так как POCO - это просто термин, используемый для сравнения простых объектов, таких как у вас, с более сложными или тяжелыми объектами, которые могли бы использоватьбольших рамок.Этот термин обычно используется в ORM для разграничения между объектами, которые являются простыми типами CLR (отсюда и акроним «Простые старые объекты CLR»), и объектами, которые наследуются от общего базового типа, специфичного для реализации.Точно так же POCO может также использоваться, чтобы указать, что сам тип не имеет прямого соединения с ORM или библиотекой, которая его использует, поэтому он может легко использоваться с другими.

Итак, короче говоря, делайте это так же, каквы описали с методами по типам.

1 голос
/ 27 октября 2011

Я мог бы поставить функции на сами объекты и наследовать их таким образом, и это прекрасно работает, но это нарушает дизайн POCO

Так что я согласен со всеми здесьэто не сломает ваш дизайн POCO.Например, это может выглядеть примерно так:

  public class BaseClass
  {
     public int Property1 { get; set; }
     public int Property2 { get; set; }
     public virtual bool Submitted() { return (Property1 != 0); }
     public virtual bool Approved() { return (Property2 != 0); }
     // ...
  }

  public class Class1 : BaseClass
  {
     public string PropertyA { get; set; }
     public string PropertyB { get; set; }
     public override bool Submitted() 
     { return !String.IsNullOrEmpty(PropertyA); }
     public override bool Approved() 
     // Or do specific Class1 Approval actions...
     { return !String.IsNullOrEmpty(PropertyB); }
     // ...
  }

  public class Class2 : BaseClass
  {
     public string PropertyC { get; set; }
     public string PropertyD { get; set; }
     public override bool Submitted()
     { return !String.IsNullOrEmpty(PropertyC); }
     public override bool Approved()
     { return !String.IsNullOrEmpty(PropertyD); }
     // ...
  }

  public abstract class WorkflowState<PocoType> 
     where PocoType : BaseClass
  {
     private WorkflowManager<PocoType> _workflowManger;
     private PocoType _pocoObject;

     public WorkflowManager<PocoType> Workflow
     {
        get { return _workflowManger; }
        set { _workflowManger = value; }
     }

     public PocoType PocoObject
     {
        get { return _pocoObject; }
        set { _pocoObject = value; }
     }

     public abstract void Submitted();
     public abstract void Approved();
     // ...
  }

  public class InitialState<PocoType> : 
     WorkflowState<PocoType> where PocoType : BaseClass
  {
     public InitialState(PocoType pocoObject)
     {
        base.PocoObject = pocoObject;
     }

     public override void Submitted()
     {
        if (PocoObject.Submitted())
        {
           // move to approved state if submitted is ok for the poco
           // definition
           Workflow.State = new ApprovedState<PocoType>(this);
        }
     }

     public override void Approved()
     {
        // Not supported state
        throw new InvalidOperationException();
     }

     // ...
  }

  public class ApprovedState<PocoType> :
     WorkflowState<PocoType> where PocoType : BaseClass
  {
     public ApprovedState(WorkflowState<PocoType> state)
     {
        base.PocoObject = state.PocoObject;
        base.Workflow = state.Workflow;
     }

     public override void Submitted()
     {
        // Not supported state
        throw new InvalidOperationException();
     }

     public override void Approved()
     {
        if (PocoObject.Approved())
        {
           // next state ...
           //Workflow.State = ...
           //Send emails
           //Do approval items
        }
     }
  }

  public class WorkflowManager<PocoType> where PocoType : BaseClass
  {
     WorkflowState<PocoType> _state;

     public WorkflowManager(PocoType pocoObject)
     {
        this._state = new InitialState<PocoType>(pocoObject);
        this._state.Workflow = this;
     }

     public WorkflowState<PocoType> State
     {
        get { return _state; }
        set { _state = value; }
     }

     public void RunWorkflow()
     {
        State.Submitted();
        State.Approved();
     }
  }

И несколько разных примеров работы могут выглядеть так:

Class1 test = new Class1();
test.PropertyA = "hello";
WorkflowManager<Class1> work_flow_man = new WorkflowManager<Class1>(test);
work_flow_man.RunWorkflow();

Class2 test2 = new Class2();
test2.PropertyC = "cool";
test2.PropertyD = "dude";
WorkflowManager<Class2> work_flow_man2 = new WorkflowManager<Class2>(test2);
work_flow_man2.RunWorkflow();
0 голосов
/ 27 октября 2011

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

...