设计模式 - 策略模式

7

我是设计模式的初学者。

假设我正在开发一个C#应用程序,用于跟踪开发团队中各成员执行的开发工作(即项目跟踪器)。

我正试图受到策略模式的启发。

因此,我正在设计我的类和接口如下:

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
{
}

我该做哪些事情来改进这个设计?


团队成员:TeamMember(我认为是拼写错误) - Patrick Desjardins
4个回答

10

首先,你所拥有的不是策略模式的一个实例。 策略模式 允许动态指定执行任务的方法。你所拥有的实际上更像是标准接口设计,通过接口继承分配职责和能力。

编辑:我们来举个例子。假设你有一组工人,还有一组任务。每个工人可以执行一个任务。这些任务可能包含 DoFoo() 和 DoBar() 等多个操作。每个工人并不知道自己将要执行哪个任务;他们只知道在到达时会执行一个任务。

因此,我们需要将工人建模为将要执行的任务。由于任务的种类繁多,我们将实现 Task 作为一个接口。

于是我们会有:

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时,会创建两个Worker。第一个Worker有一个Task1任务; 第二个Worker有一个Task2任务。为了让Worker执行他们的任务,我们在Job类上调用DoJob()方法,该方法只是在每个Worker上调用DoWork()方法,然后再在每个Worker设置的Task上调用DoTask()方法。

如果我们想要更改Worker执行的任务为Task1,则调用ChangeJobsToTask1()方法,该方法将Job包含的所有Worker对象的Task设置为Task1;此时,如果在Job对象上调用DoJob()方法,所有Worker都将执行Task1任务。同样,如果我们想将任务更改为Task2,则只需调用ChangeJobsToTask2()方法;然后当调用它们的DoWork()方法时,所有Worker将执行Task2.DoTask()。

这里抽象的重点是,Worker公开了DoWork()方法,但它们并不一定知道正在做什么工作。也就是说,Worker的任务可以互换;Worker只知道他们将要做一个任务,但具体是什么对Worker来说并不重要。


从《Head First设计模式》一书中,我发现了这个原则:“识别应用程序中变化的方面,并将它们与保持不变的方面分离开来”。你能建议我如何实现这个原则吗? - JMSA
1
@JMSA:如果不了解你的应用程序,我无法建议如何做到这一点,但我会在答案中添加信息以说明一个例子。 - Paul Sonier
从这些讨论中,我发现我的示例并不完美,无法实施策略模式。是吗? - JMSA
你能建议一个更适合这个问题的模式吗? - JMSA
1
是的,看起来你的例子并不是策略模式的好例子;我们无法充分了解你的基础领域问题,以确定是否应该实现策略模式,但我怀疑不是。 - Paul Sonier
显示剩余2条评论

2
我在你的例子中没有看到策略模式。策略模式将“策略”类(通常从具有逻辑方法的接口继承,例如“DoJob()”)作为参数,并且当调用方法时,它将通过应用先前传递的策略来执行操作,而不知道它将具体执行什么操作。
在您的示例中,您可以拥有一个所有人都继承的类,该类具有SetJob(IJobStrategy)和DoJob()方法,该方法将调用接口DoJob()(来自IJobStrategy)。然后,您可以拥有多个继承IJobStrategy的具体工作。这样,您的人员不知道工作,您可以更改工作而无需修改人员类。
您可以在此处查看示例和更多信息。

0

这更像是接口隔离原则。虽然它与策略相配合,但我会做出不同的改变。

Tester不应该是一个具体类,而应该是一个TeamMember,其CompletesJobStrategy配置为TesterCompletesJobStrategy。毕竟,阻止我测试的唯一原因是当前分配给我的团队任务。

只是作为一个讨论话题,如果我针对策略进行目标定位,我会从这样的东西开始。

interface ICompleteJobStrategy {
   void CompleteJob(IResponsible responsibleParty);
}
class TesterCompletJobStrategy : ICompleteJobStrategy {
    ...
}
class TeamMember : IResponsible {
   TeamMember(ICompleteJobStrategy strat){ .. };
   void CompleteJob() { _completeJobStrategy.CompleteJob(this) ; }
}

你不需要在接口中使用“public”。 - Jason Miesionczek

0

您可以在C#中使用"dynamic",像这样:

Method(dynamic input)

Method(DTO1 input) Method(DTO2 input) Method(DTO3 input)

无需编译时错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接