2010-04-14 25 views
5

Para mi propósito de comprensión he implementado el patrón de Cadena de Responsabilidad.C# Cadena de responsabilidad con los delegados

//Abstract Base Type 

public abstract class CustomerServiceDesk 
{ 
protected CustomerServiceDesk _nextHandler; 
public abstract void ServeCustomers(Customer _customer); 
public void SetupHadler(CustomerServiceDesk _nextHandler) 
{ 
      this._nextHandler = _nextHandler; 
} 
} 

public class FrontLineServiceDesk:CustomerServiceDesk 
{ 
    public override void ServeCustomers(Customer _customer) 
    { 
     if (_customer.ComplaintType == ComplaintType.General) 
     { 
      Console.WriteLine(_customer.Name + " Complaints are registered ; 
      will be served soon by FrontLine Help Desk.."); 
     } 

     else 
     { 
      Console.WriteLine(_customer.Name + " 
      is redirected to Critical Help Desk"); 

      _nextHandler.ServeCustomers(_customer); 
     } 

     } 
    } 
public class CriticalIssueServiceDesk:CustomerServiceDesk 
{ 
    public override void ServeCustomers(Customer _customer) 
    { 
     if (_customer.ComplaintType == ComplaintType.Critical) 
     { 
      Console.WriteLine(_customer.Name + 
      "Complaints are registered ; will be served soon 
      by Critical Help Desk"); 
     } 
     else if (_customer.ComplaintType == ComplaintType.Legal) 
     { 
      Console.WriteLine(_customer.Name + 
      "is redirected to Legal Help Desk"); 
      _nextHandler.ServeCustomers(_customer); 
     } 
    } 
} 

public class LegalissueServiceDesk :CustomerServiceDesk 
{ 
    public override void ServeCustomers(Customer _customer) 
    { 
     if (_customer.ComplaintType == ComplaintType.Legal) 
     { 
      Console.WriteLine(_customer.Name + 
      "Complaints are registered ; 
      will be served soon by legal help desk"); 
     } 
    } 
} 


public class Customer 
{ 
    public string Name { get; set; } 
    public ComplaintType ComplaintType { get; set; } 
} 


public enum ComplaintType 
{ 
    General, 
    Critical, 
    Legal 
} 

void Main() 
{ 
CustomerServiceDesk _frontLineDesk = new FrontLineServiceDesk(); 
CustomerServiceDesk _criticalSupportDesk = new CriticalIssueServiceDesk(); 
CustomerServiceDesk _legalSupportDesk = new LegalissueServiceDesk(); 
_frontLineDesk.SetupHadler(_criticalSupportDesk); 
_criticalSupportDesk.SetupHadler(_legalSupportDesk); 

Customer _customer1 = new Customer(); 
_customer1.Name = "Microsoft"; 
_customer1.ComplaintType = ComplaintType.General; 

Customer _customer2 = new Customer(); 
_customer2.Name = "SunSystems"; 
_customer2.ComplaintType = ComplaintType.Critical; 

Customer _customer3 = new Customer(); 
_customer3.Name = "HP"; 
_customer3.ComplaintType = ComplaintType.Legal; 

_frontLineDesk.ServeCustomers(_customer1); 
_frontLineDesk.ServeCustomers(_customer2); 
_frontLineDesk.ServeCustomers(_customer3); 
} 

Pregunta

sin romper la cadena de responsabilidad, ¿Cómo puedo aplicar delegados y eventos para volver a escribir el código?

Respuesta

7

Si he entendido bien ... lo que podría hacer es quitar el método SetupHandler e introducir un evento OnElevateQuery fuera el objeto CriticalHelpDesk podría controlar el evento FrontLineHelpDesk.OnElevateQuery y su objeto LegalHelpDesk podría controlar el evento CriticalHelpDesk.OnElevateQuery. El evento OnElevateQuery podría pasar al cliente en el evento args.

Ejemplo

public abstract class CustomerServiceDesk 
{ 
    public delegate void ElevateQueryEventHandler(Customer c); 
    public event ElevateQueryEventHandler OnElevateQuery; 
    public abstract void ServeCustomer(Customer c); 
} 

public class FrontLineServiceDesk : CustomerServiceDesk 
{ 
    public override void ServeCustomer(Customer c) 
    { 
     switch (c.ComplaintType) 
     { 
      case ComplaintType.General: 
       Console.WriteLine(c.Name + " Complaints are registered; will be served soon by FrontLine Help Desk"); 
       break; 
      default: 
       OnElevateQuery(c); 
     } 
    } 
} 

public class CriticalIssueServiceDesk : CustomerServiceDesk 
{ 
    public override void ServeCustomer(Customer c) 
    { 
     switch (c.ComplaintType) 
     { 
      case ComplaintType.Critical: 
       Console.WriteLine(c.Name + " Complaints are registered; will be served soon by Critical Help Desk"); 
       break; 
      case ComplaintType.Legal: 
       OnElevateQuery(c); 
       break; 
      default: 
       Console.WriteLine("Unable to find appropriate help desk for your complaint."); 
       break; 
     } 
    } 
} 

public class LegalIssueServiceDesk : CustomerServiceDesk 
{ 
    public override void ServeCustomer(Customer c) 
    { 
     if (c.CompliantType == CompliantType.Legal) 
     { 
      Console.WriteLine(c.Name + " Complaints are registered; will be served soon by Legal Help Desk"); 
     } 
     else 
     { 
      // you could even hook up the FrontLine.ServeCustomer event 
      // to the OnElevateQuery event of this one so it takes the 
      // query back to the start of the chain (if it accidently ended up here). 
      Console.WriteLine("Wrong department"); 
     } 
    } 
} 

Uso

CustomerServiceDesk _frontLine = new FrontLineServiceDesk(); 
CustomerServiceDesk _criticalLine = new CriticalLineServiceDesk(); 
CustomerServiceDesk _legalLine = new LegalLineServiceDesk(); 
// hook up events 
_frontLine.OnElevateQuery += _critialLine.ServeCustomer; 
_criticalLine.OnElevateQuery += _legalLine.ServeCustomer; 

Customer _customer1 = new Customer(); 
_customer1.Name = "Microsoft"; 
_customer1.ComplaintType = ComplaintType.General; 

Customer _customer2 = new Customer(); 
_customer2.Name = "SunSystems"; 
_customer2.ComplaintType = ComplaintType.Critical; 

Customer _customer3 = new Customer(); 
_customer3.Name = "HP"; 
_customer3.ComplaintType = ComplaintType.Legal; 

_frontLine.ServeCustomer(_customer1); 
_frontLine.ServeCustomer(_customer2); 
_frontLine.ServeCustomer(_customer3); 

Sin embargo, como el tipo de consulta se basa en la enumeración ComplaintType Ha considerado el uso quizás un HelpDeskFactory que podría devolver una interfaz genérica, por ejemplo, IHelpDesk. Parece que también podría usar el patrón de estrategia para este ejemplo en particular.

+0

Excelente Explicación, Gracias por sugerir HelpDeskFactory. – user274364

+0

@nettguy: no hay problema :) – James

2

Cliente que tiene un tipo de reclamo parece un atributo extraviado. Supongo que quiere decir que una Queja tiene un Tipo.

Puedo estar equivocado, en cuyo caso puede señalar qué comportamiento falta Esto me parece un evento. Cada controlador de eventos se llamaría por orden de suscripción. Cada controlador es libre de ignorar la notificación basada en la queja. Se llama al siguiente controlador siempre que la propiedad Handled de eventArgs sea falsa y haya suscriptores pendientes.

class ComplaintSource 
{ 
    public delegate void ComplaintHandler(Complaint complaint, HandledEventArgs evtArgs); 
    public event ComplaintHandler NewComplaint; 

    // code that raises the NewComplaint event as appropriate. 
    public void DoStuffThatRaisesTheEvent() 
    { 
     var evtArgs = new HandledEventArgs(); 
     var theComplaint = new Complaint(); 
     if (null == this.NewComplaint) 
      return; 

     Delegate[] list = NewComplaint.GetInvocationList(); 
     foreach (Delegate del in list) 
     { 
      if (evtArgs.Handled) 
       break; 
      ComplaintHandler handler = (ComplaintHandler)del; 
      handler(theComplaint, evtArgs); 
     } 
    } 
} 

class FrontLineServiceDesk 
{ 
    FrontLineServiceDesk(ComplaintSource source) 
    { source.NewComplaint += HandleGeneralComplaint; } 
    void HandleGeneralComplaint(Complaint complaint, HandledEventArgs evtArgs) { ... 
    // set evtArgs.Handled = true if you've handled the complaint 
    // this will stop the chain 
    } 
} 

class CriticalIssueServiceDesk 
{ 
    CriticalIssueServiceDesk(ComplaintSource source) 
    { source.NewComplaint += HandleGeneralComplaint; } 
    void HandleCriticalComplaint(Complaint complaint, HandledEventArgs evtArgs) { ... } 
} 

// finally set the ball in motion 

var source = new CompaintSource(); 
var frontLineDesk = new FrontLineServiceDesk(source); 
var criticalIssueDesk = new CriticalIssueServiceDesk(source); 

source.DoStuffThatRaisesTheEvent(); 
+3

Está tratando de entender la cadena de responsabilidad, no escribir el mejor código para hacer este trabajo. –

+0

@Gishu Muchas gracias – user274364

-1

Esto es muy similar a las respuestas anteriores, pero más simplificado. :)

public abstract class CustomerServiceDesk 
{ 
    protected CustomerServiceDesk() 
    { 
     ServeCustomers = doServeCustomers; 
    } 

    protected CustomerServiceDesk m_ServiceDesk = null; 
    protected abstract void doServeCustomers(Customer _customer); 

    public delegate void ServeCustomersDelegate(Customer _customer); 
    public ServeCustomersDelegate ServeCustomers = null; 
} 

public class LegalissueServiceDesk : CustomerServiceDesk 
{ 
    public LegalissueServiceDesk() 
    { 
    } 

    protected override void doServeCustomers(Customer _customer) 
    { 
     if (_customer.ComplaintType == ComplaintType.Legal) 
     { 
      Console.WriteLine(_customer.Name + " - Complaints are registered ; will be served soon by legal help desk.\n"); 
     } 
    } 
} 

public class CriticalIssueServiceDesk : CustomerServiceDesk 
{ 
    public CriticalIssueServiceDesk() 
    { 
     m_ServiceDesk = new LegalissueServiceDesk(); 
     ServeCustomers += m_ServiceDesk.ServeCustomers; 
    } 

    protected override void doServeCustomers(Customer _customer) 
    { 
     if (_customer.ComplaintType == ComplaintType.Critical) 
     { 
      Console.WriteLine(_customer.Name + " - Complaints are registered ; will be served soon by Critical Help Desk.\n"); 
     } 
    } 
} 

public class FrontLineServiceDesk : CustomerServiceDesk 
{ 
    public FrontLineServiceDesk() 
    { 
     m_ServiceDesk = new CriticalIssueServiceDesk(); 
     ServeCustomers += m_ServiceDesk.ServeCustomers; 
    } 

    protected override void doServeCustomers(Customer _customer) 
    { 
     if (_customer.ComplaintType == ComplaintType.General) 
     { 
      Console.WriteLine(_customer.Name + " - Complaints are registered ; will be served soon by FrontLine Help Desk.\n"); 
     } 
    } 
} 

public class Customer 
{ 
    public string Name; 
    public ComplaintType ComplaintType; 
} 

public enum ComplaintType 
{ 
    General, 
    Critical, 
    Legal 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Customer _customer1 = new Customer(); 
     _customer1.Name = "Microsoft"; 
     _customer1.ComplaintType = ComplaintType.General; 

     Customer _customer2 = new Customer(); 
     _customer2.Name = "SunSystems"; 
     _customer2.ComplaintType = ComplaintType.Critical; 

     Customer _customer3 = new Customer(); 
     _customer3.Name = "HP"; 
     _customer3.ComplaintType = ComplaintType.Legal; 

     FrontLineServiceDesk _frontLineDesk = new FrontLineServiceDesk(); 

     _frontLineDesk.ServeCustomers(_customer1); 
     _frontLineDesk.ServeCustomers(_customer2); 
     _frontLineDesk.ServeCustomers(_customer3); 

     Console.In.ReadLine(); 
    } 
} 
+0

Me encanta el hecho de que la versión optimizada es más larga que el resto. En el lado de la broma, esta versión ha codificado las etapas. p.ej. para agregar un nuevo paso intermedio en la cadena, tendría que modificar una clase existente; lo que es más importante, cada etapa tiene conocimiento sobre la siguiente etapa (y la contiene/la crea en este caso). Aflojaría el acoplamiento aquí ... – Gishu

+0

Sí, estoy de acuerdo. Es más de cambio de diseño. Puede parecer lógico decirlo de esta manera ... pero las necesidades pueden diferir. Acabo de presentar mi idea. En verdad, depende de la necesidad del diseño. Su ejemplo hace el trabajo lo suficientemente bien como para demostrar el uso de delegados. Por cierto, el código puede parecer más largo que el tuyo porque lo has omitido en su mayoría = D – Nayan

+0

-1: Creo que te falta el código para derivar la queja al siguiente nivel de responsabilidad. Avísame si no estás de acuerdo. –