2009-06-19 9 views
6

Je suis un débutant en Design Patterns. Supposons que je développe une application C# pour suivre les travaux de développement effectués par différents membres de l'équipe de développement (c'est-à-dire un Project Tracker). J'essaie d'être inspiré par Strategy Pattern.Design Patterns - Modèle de stratégie

Alors je conçois mes classes et interfaces comme suit:

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

Que devrais-je faire pour améliorer cette conception?

+0

TeamMamber: d'équipe (sort erreur je pense) –

Répondre

8

Eh bien, tout d'abord, ce que vous avez là n'est pas une instance d'un modèle de stratégie. Le Strategy Pattern permet la spécification dynamique d'une méthode pour faire les choses. Ce que vous avez ici est vraiment plus d'une conception d'interface standard, où vous allouez des responsabilités et des capacités par l'héritage d'interface.

Édition: Utilisons un exemple. Disons que vous avez un groupe de travailleurs; vous avez également un ensemble de tâches. Chaque travailleur peut effectuer une tâche. Ces tâches peuvent consister en plusieurs choses, telles que DoFoo() et DoBar(). Chaque travailleur ne sait pas quelle tâche ils vont effectuer; ils savent juste quand ils se présentent qu'ils feront une tâche. Donc, nous allons vouloir modéliser les travailleurs comme ayant une tâche qu'ils vont effectuer. Étant donné que les tâches varient considérablement, nous allons implémenter la tâche en tant qu'interface.

Nous aurons donc:

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

Alors qu'est-ce qui se passe est que lorsque nous instancier un Job, le Job crée deux Worker s. Le premier Worker a une tâche Task1; la deuxième Worker a une tâche Task2. Pour les Worker de faire leur Task s, nous appelons la méthode DoJob() de la classe Job, qui appelle simplement la méthode DoWork() sur chacun des Worker s, ce qui appelle la méthode DoTask() sur chacun des Task s que les Worker s ont été mis avec.

Si nous voulons changer les Worker s à tous faire Task1, nous appelons la méthode ChangeJobsToTask1(), qui fixe le Task à Task1 pour tous les objets contenus Worker par le Job; Si, à ce moment, nous appelons DoJob() sur l'objet Job, tous les Worker exécuteront la tâche Task1. De même, si nous voulons changer le en Task2, il suffit d'appeler la méthode ChangeJobsToTask2(); tous les Worker exécuteront alors Task2.DoTask() lorsque leur méthode DoWork() sera appelée.

Le point important de l'abstraction ici est que les Worker s exposent une méthode DoWork(), mais ils ne savent pas nécessairement quel travail il est fait. Autrement dit, les Task s pour les Worker sont interchangeables; le Worker s savent juste qu'ils vont faire un Task, mais les détails de ce qu'il est sont sans importance pour le Worker s.

+0

Extrait du livre Head First Design Patterns, j'ai trouvé le principe « Identifier les aspects de votre application qui varient et les séparer de ce qui reste le même ». Pouvez-vous me suggérer comment dois-je accomplir ceci? –

+1

@JMSA: Je ne peux pas suggérer comment faire cela sans tout savoir sur votre application, mais je vais ajouter des informations à la réponse pour illustrer un exemple. –

+0

De toutes ces discussions, j'ai effectivement trouvé que, mon exemple n'est pas parfait pour le schéma de stratégie à mettre en œuvre. N'est-ce pas? –

2

Je ne vois pas le modèle de stratégie dans votre exemple. Le modèle de stratégie prend la classe "stratégie" (hérite généralement d'une interface avec une méthode logique, exemple "DoJob()") dans le paramètre et quand une méthode est appelée, elle va opérer en appliquant la stratégie précédente sans savoir ce qu'elle va concrètement faire. Dans votre exemple, vous pourriez avoir une classe héritée par tous vos utilisateurs qui ont un SetJob (IJobStrategy) et une méthode DoJob() qui appellera l'interface DoJob() (à partir de IJobStrategy). Ensuite, vous pouvez avoir plusieurs tâches concrètes qui héritent de IJobStrategy. De cette façon, vos employés ne connaissent pas le travail et vous pouvez changer de travail sans avoir à modifier la classe de personne.

Vous pouvez voir l'exemple et plus d'informations here.

0

Cela ressemble beaucoup plus au principe de ségrégation de l'interface. Maintenant, ça joue bien avec la stratégie, mais voici ce que je ferais différemment.

Le testeur ne serait pas une classe Concrete, ce serait un TeamMember avec une TesterCompletesJobStrategy configurée comme CompletesJobStrategy. Après tout, la seule chose qui m'empêche de tester est ma tâche actuellement assignée dans l'équipe. En guise de discussion, je commencerais par quelque chose de plus similaire si je ciblais une stratégie.

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

Vous n'avez pas besoin du 'public' dans l'interface –