Design Patterns - Strategy Pattern
Asked Answered
H

4

7

I am a beginner in Design Patterns.

Suppose I am developing a C# application to track the development works performed by various members in development team (i.e. a Project Tracker).

I am trying to be inspired by Strategy Pattern.

So I am designing my classes and interfaces as follows:

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

What things should I do to improve this design?

Hosey answered 19/6, 2009 at 16:38 Comment(1)
TeamMamber : TeamMember (spell error I think)Noxious
P
10

Well, first off, what you have there is not an instance of a Strategy pattern. The Strategy Pattern allows for the dynamic specification of a method for getting things done. What you have here is really more of a standard interface design, where you allocate responsibilities and abilities by interface inheritance.

Edit: Let's use an example. Let's say that you have a group of Workers; you also have a set of Tasks. Each Worker can perform a Task. These Tasks can consist if several things, such as DoFoo() and DoBar(). Each Worker does not know what Task they will perform; they just know when they show up that they will do a Task.

So we'll want to model Workers as having a Task that they will perform. Since the Tasks vary widely, we'll implement the Task as an interface.

So we'll have:

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();
      }
   }
}

So what happens is that when we instantiate a Job, the Job creates two Workers. The first Worker has a Task1 task; the second Worker has a Task2 task. To make the Workers do their Tasks, we call the DoJob() method on the Job class, which just calls the DoWork() method on each of the Workers, which in turn calls the DoTask() method on each of the Tasks that the Workers were set with.

If we want to change the Workers to all do Task1, we call the ChangeJobsToTask1() method, which sets the Task to Task1 for all of the Worker objects contained by the Job; if, at that point, we call the DoJob() on the Job object, all the Workers will perform the Task1 task. Similarly, if we want to change the Tasks to Task2, just call the ChangeJobsToTask2() method; all the Workers will then execute the Task2.DoTask() when their DoWork() method is called.

The important point of abstraction here is that the Workers expose a DoWork() method, but they do not necessarily know what work it is that is being done. That is, the Tasks for the Workers are interchangeable; the Workers just know that they're going to do a Task, but the specifics of what it is are unimportant to the Workers.

Protanopia answered 19/6, 2009 at 16:45 Comment(7)
From the book Head First Design Patterns, I found the principle "Identify the aspects of your application that vary and separate them from what stays the same". Can you suggest me how should I accomplish this?Alwitt
@JMSA: I can't suggest how to do that without knowing all about your application, but I'll add information to the answer to illustrate an example.Protanopia
From all these discussions, I actually found that, my example is not perfect for the Strategy Pattern to be implemented. Isn't it?Alwitt
Can you suggest a pattern that will be more fit for this problem?Alwitt
Yes, it appears that your example is not a good example of the Strategy Pattern; we can't really tell enough about your underlying domain problem to tell if the Strategy Pattern would be the right one to implement for it, but I'd suspect not.Protanopia
Just consider it as a Project Tracker app. Now what can you suggest?Alwitt
@JMSA: again, I just don't know enough about the specifics of your application to be able to suggest. You might try asking another question of the general audience here on SO... In general, though, it's not easy to figure out how a design breakdown for a specific problem should be; that's precisely why Software Engineering is hard.Protanopia
N
2

I do not see the strategy pattern in your example. Strategy pattern take the "strategy" class (usually inherit from an Interface with a logic method, example "DoJob()") in parameter and when a method is called, it will do operation on by applying the strategy passing earlier without knowing what it will concretly do.

In your example you could have a class that all your people inherit that have a SetJob(IJobStrategy) and have a DoJob() method that will call the interface DoJob() (from IJobStrategy). Than, you can have multiple concrete job that inherit IJobStrategy. This way, your people do not know the job and you can change the job without having to modify the person class.

You can see example and more information here.

Noxious answered 19/6, 2009 at 16:44 Comment(0)
I
0

This seems much more like the Interface Segregation Principle. Now, that does play well with strategy, but here's what I'd make different.

Tester wouldn't be a Concrete class, it would be a TeamMember with a TesterCompletesJobStrategy configured as it's CompletesJobStrategy. After all, the only thing keeping me from testing is my currently assigned task on the team.

Just as a talking point, I'd start with something more like this if I was targeting a Strategy.

interface ICompleteJobStrategy {
   void CompleteJob(IResponsible responsibleParty);
}
class TesterCompletJobStrategy : ICompleteJobStrategy {
    ...
}
class TeamMember : IResponsible {
   TeamMember(ICompleteJobStrategy strat){ .. };
   void CompleteJob() { _completeJobStrategy.CompleteJob(this) ; }
}
Insecure answered 19/6, 2009 at 16:53 Comment(1)
You don't need the 'public' in the interfaceIntensive
K
0

u can use "dynamic" in c# instead like this:

Method(dynamic input)

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

without complie time error

Karenkarena answered 7/8, 2020 at 14:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.