2009-06-19 14 views
6

Sono un principiante in Design Patterns.Design Patterns - Strategy Pattern

Supponiamo che stia sviluppando un'applicazione C# per tracciare i lavori di sviluppo eseguiti da vari membri nel team di sviluppo (ovvero un Project Tracker).

Sto cercando di ispirarmi a Strategy Pattern.

Così sto progettando le mie classi e interfacce come segue:

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

Quali cose devo fare per migliorare questo disegno?

+0

TeamMamber: membro del team (spell errore credo) –

risposta

8

Beh, per prima cosa, quello che hai non è un'istanza di un modello di strategia. Lo Strategy Pattern consente la specifica dinamica di un metodo per ottenere risultati. Quello che avete qui è davvero più di un design di interfaccia standard, in cui si assegnano responsabilità e abilità tramite l'ereditarietà dell'interfaccia.

Modifica: Facciamo un esempio. Diciamo che hai un gruppo di lavoratori; hai anche una serie di compiti. Ogni lavoratore può eseguire un compito. Queste attività possono consistere in diverse cose, come DoFoo() e DoBar(). Ogni lavoratore non sa quale attività eseguirà; sanno solo quando si presentano che faranno un compito.

Quindi vorremmo modellare i lavoratori come se avessero un compito che eseguiranno. Poiché le attività variano ampiamente, implementeremo il compito come interfaccia.

Così avremo:

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

Quindi, quello che succede è che quando abbiamo un'istanza di un Job, il Job crea due Worker s. Il primo Worker ha un'attività Task1; il secondo Worker ha un'attività Task2. Per rendere i Worker s fanno loro Task s, che noi chiamiamo il metodo DoJob() sulla classe Job, che ha appena chiama il metodo DoWork() su ciascuno dei Worker s, che a sua volta chiama il metodo DoTask() su ciascuno dei Task s che i Worker s sono stati impostati con.

Se vogliamo cambiare le Worker s a tutti fare Task1, chiamiamo il metodo ChangeJobsToTask1(), che imposta il Task per Task1 per tutti gli oggetti contenuti Worker dal Job; se, a quel punto, chiamiamo lo DoJob() sull'oggetto Job, tutti i numeri Worker eseguiranno l'attività Task1. Allo stesso modo, se vogliamo cambiare il s a Task2, basta chiamare il metodo ChangeJobsToTask2(); tutti gli Worker eseguiranno quindi il Task2.DoTask() quando viene chiamato il loro metodo DoWork().

Il punto importante di astrazione qui è che il Worker s espone un metodo DoWork(), ma non necessariamente sapere che lavoro è che viene fatto. Cioè, gli Task s per gli Worker s sono intercambiabili; i Worker s sanno solo che stanno andando a fare un Task, ma le specifiche di ciò che è non sono importanti per il Worker s.

+0

Dal libro Head First Design Patterns, ho trovato il principio "Identificare gli aspetti della vostra applicazione che variano e separarli da quello che rimane lo stesso". Potete suggerirmi come devo realizzare questo? –

+1

@ JMSA: Non posso suggerire come farlo senza sapere tutto sulla tua applicazione, ma aggiungerò informazioni alla risposta per illustrare un esempio. –

+0

Da tutte queste discussioni, ho effettivamente trovato che, il mio esempio non è perfetto per il modello di strategia da implementare. Non è vero? –

2

Non vedo lo schema di strategia nell'esempio. Il modello strategico prende la classe "strategia" (di solito eredita da un interfaccia con un metodo logico, esempio "DoJob()") in parametro e quando viene chiamato un metodo, eseguirà l'operazione applicando la strategia che passa prima senza sapere cosa farà concretamente.

Nel tuo esempio potresti avere una classe che tutte le persone ereditano che hanno un SetJob (IJobStrategy) e hanno un metodo DoJob() che chiamerà l'interfaccia DoJob() (da IJobStrategy). Di conseguenza, è possibile avere più lavori concreti che ereditano IJobStrategy. In questo modo, i tuoi dipendenti non conoscono il lavoro e puoi modificare il lavoro senza dover modificare la classe di persone.

È possibile visualizzare esempi e ulteriori informazioni here.

0

Questo sembra molto più simile al principio di segregazione dell'interfaccia. Ora, questo gioca bene con la strategia, ma ecco cosa farei di diverso.

Tester non è una classe Concrete, sarebbe un TeamMember con un TesterCompletesJobStrategy configurato come CompletesJobStrategy. Dopotutto, l'unica cosa che mi impedisce di provare è il mio compito attualmente assegnato al team.

Proprio come punto di discussione, inizierei con qualcosa di più simile a questo se stessi prendendo di mira una strategia.

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

Non è necessario il "pubblico" nell'interfaccia –