C# 中使用委托实现责任链模式

5

为了加强自己的理解,我实现了职责链模式(Chain-of-Responsibility pattern)。

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

问题

在不打破责任链的情况下,如何应用委托和事件重写代码?

3个回答

8
如果我理解您的意思正确的话...您可以删除SetupHandler方法,并引入一个OnElevateQuery事件,您的CriticalHelpDesk对象可以处理FrontLineHelpDesk.OnElevateQuery事件,而您的LegalHelpDesk对象可以处理CriticalHelpDesk.OnElevateQuery事件。 OnElevateQuery事件可以通过事件参数传递客户端。
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");
        }
    }
}

使用方法

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

然而,由于查询类型基于枚举ComplaintType,您是否考虑使用HelpDeskFactory来返回通用接口,例如IHelpDesk。听起来您还可以在这个特定的例子中使用策略模式


非常好的解释,感谢您推荐HelpDeskFactory。 - user274364

2
客户投诉类型似乎是一个放错位置的属性。我认为你的意思是一个投诉具有类型。
如果我错了,你可以指出缺少哪些行为。 这对我来说只是一个事件。每个事件处理程序将按订阅顺序调用。每个处理程序可以根据投诉情况忽略通知。只要 EventArgs 的 Handled 属性为 false 且仍有待处理的订阅者,就会调用下一个处理程序。
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
他正在努力理解责任链模式,而不是编写最佳代码来完成此任务。 - Steven Mackenzie

-1

这个与上面的答案非常相似,但更加简洁。:)

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

喜欢这个简化版本比其他版本更长的事实。说真的,这个版本已经将阶段硬编码了。例如,要在链中添加一个新的中间步骤,您必须修改现有的类 - 更重要的是,每个阶段都知道下一个阶段(在这种情况下包含/创建它)。我会在这里放松耦合。 - Gishu
是的,我同意。这更多是设计上的改变。这种方式可能看起来很合乎逻辑...但需求可能有所不同。我只是提出了我的想法。事实上,它取决于设计的需求。您的示例已经足够好地完成了演示委托的使用。顺便说一句,代码可能看起来比您的代码长,因为您省略了大部分内容 =D - Nayan
-1:我认为你缺少将投诉上升到下一个责任级别的代码。如果你不同意,请让我知道。 - Jim G.
你只是增加了类的依赖性。 - mo.

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