Шаблоны проектирования — шаблон стратегии

Я новичок в шаблонах проектирования.

Предположим, я разрабатываю приложение C# для отслеживания работ по разработке, выполняемых различными членами команды разработчиков (т. е. Project Tracker).

Я пытаюсь вдохновиться шаблоном стратегии.

Поэтому я разрабатываю свои классы и интерфейсы следующим образом:

interface IEmployee
{
    void Retires();
    void TakesLeave();
}

interface IResponsible
{
void AcknowledgeJobAccomplish();
void CompletesJob();
}

interface ILeader
{
    void FormsTeam();
    void RecruitsNewMember();
    void KicksOutMemberFromTheTeam();
    void AssignsJob();
void UnassignsJob();
void QueriesTheJobStatus();
void ChangesTheJobStatus();
}

interface IPersistent
{
    void Save();
    void Update();
    void Delete();  
}

abstract class TeamMember : IEmployee, IResponsible, IPersistent
{
    string Name;
}

class Programmer : TeamMember
{
}

class LeadProgrammer : Programmer, ILeader
{
    ProgrammerCollection associateProgrammers;
}

class ProjectManager :  TeamMember, ILeader
{
    TeamMemberCollection teamMembers;
}

abstract class Tester : TeamMember
{
}

class UnitTester : Tester
{
}

class QC : Tester
{
}

class SupportStaff : TeamMember
{
}

Что я должен сделать, чтобы улучшить этот дизайн?


person user366312    schedule 19.06.2009    source источник
comment
TeamMamber : TeamMember (я думаю, ошибка в написании)   -  person Patrick Desjardins    schedule 19.06.2009


Ответы (4)


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

Изменить: давайте использовать пример. Допустим, у вас есть группа Рабочих; у вас также есть набор задач. Каждый работник может выполнять задачу. Эти задачи могут состоять из нескольких вещей, таких как DoFoo() и DoBar(). Каждый Рабочий не знает, какую Задачу он будет выполнять; они просто знают, когда появляются, что выполнят Задание.

Итак, мы хотим смоделировать Рабочих как имеющих Задачу, которую они будут выполнять. Поскольку задачи сильно различаются, мы реализуем задачу как интерфейс.

Итак, у нас будет:

public class Worker 
{
   public Task myTask;

   public Worker(Task task)
   {
      myTask = task;
   }

   public void DoWork() 
      {
      myTask.DoTask();
      }
   }
}

Interface Task
{
   void DoTask();
}

public class Task1 : Task
{
   public void DoTask()
   {
   // Do whatever Task1 will do
   }
}

public class Task2 : Task
{
   public void DoTask()
   {
   // Do whatever Task2 will do
   }
}

public class Job
{
   public List<Worker> workers;

   public void Job()
   {
      workers.Add(new Worker(new Task1()));
      workers.Add(new Worker(new Task2()));
   }

   public void DoJob()
   {
      foreach (Worker worker in workers)
      {
      worker.DoWork();
      }
   }

   public void ChangeJobsToTask1()
   {
      foreach (Worker worker in workers)
      {
         worker.myTask = new Task1();
      }
   }

   public void ChangeJobsToTask2()
   {
      foreach (Worker worker in workers)
      {
         worker.myTask = new Task2();
      }
   }
}

Итак, когда мы создаем экземпляр Job, Job создает два Worker. Первый Worker имеет задачу Task1; у второго Worker задача Task2. Чтобы заставить Worker выполнять свои Task, мы вызываем метод DoJob() для класса Job, который просто вызывает метод DoWork() для каждого из Worker, который, в свою очередь, вызывает метод DoTask() для каждого из Task, с которыми были установлены Worker.

Если мы хотим изменить Workers на все действия Task1, мы вызываем метод ChangeJobsToTask1(), который устанавливает Task в Task1 для всех объектов Worker, содержащихся в Job; если в этот момент мы вызовем DoJob() для объекта Job, все Worker будут выполнять задачу Task1. Точно так же, если мы хотим изменить Tasks на Task2, просто вызовите метод ChangeJobsToTask2(); все Worker будут выполнять Task2.DoTask() при вызове их метода DoWork().

Важным моментом абстракции здесь является то, что Worker раскрывают метод DoWork(), но они не обязательно знают, какая работа выполняется. То есть Tasks для Workers взаимозаменяемы; Workers просто знают, что они собираются сделать Task, но особенности того, что это такое, не важны для Workers.

person Paul Sonier    schedule 19.06.2009
comment
Из книги Head First Design Patterns я нашел принцип Определите аспекты вашего приложения, которые различаются, и отделите их от того, что остается неизменным. Можете ли вы предложить мне, как я должен это сделать? - person ; 19.06.2009
comment
@JMSA: я не могу предложить, как это сделать, не зная всего о вашем приложении, но я добавлю информацию в ответ, чтобы проиллюстрировать пример. - person Paul Sonier; 19.06.2009
comment
Из всех этих обсуждений я действительно обнаружил, что мой пример не идеален для реализации шаблона стратегии. Не так ли? - person ; 19.06.2009
comment
Можете ли вы предложить шаблон, который будет более подходящим для этой проблемы? - person ; 19.06.2009
comment
Да, похоже, что ваш пример не является хорошим примером шаблона стратегии; мы не можем рассказать достаточно о вашей основной проблеме домена, чтобы сказать, будет ли шаблон стратегии правильным для ее реализации, но я подозреваю, что нет. - person Paul Sonier; 19.06.2009
comment
Просто считайте это приложением Project Tracker. Теперь, что вы можете предложить? - person ; 19.06.2009
comment
@JMSA: опять же, я просто недостаточно знаю специфику вашего приложения, чтобы предложить. Вы можете попробовать задать другой вопрос широкой аудитории здесь, на SO... В общем, не так просто понять, как должна быть разбивка дизайна для конкретной проблемы; именно поэтому программная инженерия сложна. - person Paul Sonier; 19.06.2009

Я не вижу шаблона стратегии в вашем примере. Шаблон стратегии принимает класс «стратегия» (обычно наследуется от интерфейса с логическим методом, например «DoJob()») в параметре, и когда метод вызывается, он выполняет операцию, применяя стратегию, переданную ранее, не зная, что это такое. конкретно сделаю.

В вашем примере у вас может быть класс, который наследуют все ваши люди, который имеет SetJob(IJobStrategy) и имеет метод DoJob(), который будет вызывать интерфейс DoJob() (из IJobStrategy). Затем у вас может быть несколько конкретных заданий, которые наследуют IJobStrategy. Таким образом, ваши люди не знают работу, и вы можете сменить работу, не изменяя класс человека.

Вы можете увидеть пример и дополнительную информацию здесь.

person Patrick Desjardins    schedule 19.06.2009

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

Tester не будет классом Concrete, это будет TeamMember с TesterCompletesJobStrategy, настроенным как CompletesJobStrategy. В конце концов, единственное, что удерживает меня от тестирования, — это моя текущая задача в команде.

Просто в качестве темы для разговора я бы начал с чего-то более похожего, если бы я нацеливался на Стратегию.

interface ICompleteJobStrategy {
   void CompleteJob(IResponsible responsibleParty);
}
class TesterCompletJobStrategy : ICompleteJobStrategy {
    ...
}
class TeamMember : IResponsible {
   TeamMember(ICompleteJobStrategy strat){ .. };
   void CompleteJob() { _completeJobStrategy.CompleteJob(this) ; }
}
person Ball    schedule 19.06.2009
comment
Вам не нужен «общедоступный» в интерфейсе - person Jason Miesionczek; 19.06.2009

вместо этого вы можете использовать dynamic в С# следующим образом:

Метод (динамический ввод)

Метод (вход DTO1) Метод (вход DTO2) Метод (вход DTO3)

без ошибки времени выполнения

person mostafa kazemi    schedule 07.08.2020