在责任链模式中使用共享组件的最佳方式

本文关键字:组件 共享 最佳 方式 责任 模式 | 更新日期: 2023-09-27 18:24:44

我对责任链模式有问题。所有处理程序都实现这个类:

/// <summary>
/// Chain of Responsibility pattern
/// </summary>
abstract public class ChainHandler
{
    protected LogWrapper Log;
    protected ChainHandler successor;
    /// <summary>
    /// SetSuccessor
    /// </summary>
    private void SetSuccessor(ChainHandler successor)
    {
       this.successor = successor;
    }
    protected oAPI NotImplemented
    {
        get { return new oAPI(HTTPCodes.NotImplemented); }
    }
    /// <summary>
    /// Set Successor to the end of chain
    /// </summary>
    /// <param name="successor">Handler</param>
    public void Add(ChainHandler successor)
    {
        if (this.successor == null) SetSuccessor(successor);
        else
        {
            this.successor.Add(successor);
        }
    }
    protected oAPI ReferToSuccessor (iAPI request)
    {
        if (successor != null) return successor.HandleRequest(request);
        return NotImplemented;
    }
    /// <summary>
    /// output API builder
    /// </summary>
    /// <param name="code">HTTP code</param>
    /// <returns></returns>
    protected oAPI RB(HTTPCodes code, string messsage = null, string data = null, bool hasError = false)
    {
        return new oAPI(Shared.User, code, message) { Data = data, HasError = hasError };
    }
    /// <summary>
    /// Serializer (JSON)
    /// </summary>
    public Func<object, string> Serializer { get; set; }
    /// <summary>
    /// Handle request
    /// </summary>
    /// <param name="request">request</param>
    /// <returns>result</returns>
    public abstract oAPI HandleRequest(iAPI request);
}

然后我实现了DocHandler

public class DocHandler:ChainHandler
    {
     public DocChain()
     {
      Log = new LogWrapper(this);
     }
     public override oAPI HandleRequest(iAPI request)
     {
      switch (request.Comand)
      {
       case iAPI.Target.GetModel:
        return GetModel(request.DocID);
      }
      return ReferToSuccessor(request);
      }
 private Doc GetDoc(int id)
    {
     Log.Debug("get document by id: " + id);
     using (var unit = UnitOfWork.Create())
     {
      var repository = unit.GetRepository<Doc>();
      Doc doc = repository.Get(id);
      if (doc == null)
      {
       Log.Error("Document not found");
       throw new DocumentNotFoundException();
      }
      return doc;
     }
    }
 public oAPI GetModel(int DocId)
      {
       var Model = GetDoc();
       return RB(HTTPCodes.OK, data: Serializer(
       Model));
       }
}

CloudHandler

 public class CloudHandler:ChainHandler
    {
        private IDAVService _service;
        private string RemoteRepository;
        public CloudChain(IDAVService service)
        {
            Log=new LogWrapper(this);
            _service=service;
        }
        public override oAPI HandleRequest(iAPI request)
        {
            switch (request.Comand)
                    {
                    case iAPI.Target.UploadModel:
                    return Upload(request.DocID,request.VersionID);
                    case iAPI.Target.DownloadModel:
                    return Download(request.VersionID, request.DocID);
                    }
            return ReferToSuccessor(request);
        }
        public oAPI Upload(int DocID,int VersionID)
        {
            // Need to get model from DocHandler
            var model = ???
            service.Upload(model);
            return RB(HTTPCodes.OK);
        }
        public oAPI Download(int DocID,int VersionID)
        {
            // Need to get model from DocHandler
            var model = ???
            service.Download(model);
            return RB(HTTPCodes.OK);
        }
    }

我的问题是找到在处理程序之间共享方法和属性的最佳方式。现在我使用静态类SharedComponents,其中每个处理程序都委托自己的方法。

public static class SharedComponents
    {
        public static Func<int, Doc> GetDoc;
    }

在DocHandler中,我委托方法SharedComponents.GetDoc = this.GetDoc;,然后在CloudHandler var Model = SharedComponents.GetDoc(docid)中使用它。这是spagetti为数百个共享方法编写委托。

但我是如何测试的?我必须初始化所有处理程序(因为A使用B的方法,B可能使用C的方法等),以便在一个处理程序中测试一个方法。恐怖

我尝试将共享方法设置为静态,以便像var Model = DocHandler .GetDoc(docid)一样使用。但这种解决方案打破了依赖反转原理。如果某个共享方法使用上下文(如UnitOfWork中的session),我需要在测试中再次初始化所有处理程序!

在责任链模式中使用共享组件的最佳方式

应用响应链模式的主要目的之一是具有一些相互独立的步骤。目标是能够更改步骤的顺序或添加新步骤,而不必更改现有步骤。

在您的方法中,即使在共享类上使用委托,也会引入从CloudHandlerDocHandler的强依赖关系,因为只有设置了委托,它才能运行。

为了解决这个问题,我建议从Handlers中提取共享方法,并将它们移动到一个或多个具有虚拟方法或接口的助手类中。在构造过程中将所需的实例注入到Handlers中,而不是在Handlers中创建类的实例。您提到了数百种共享方法。重要的是要以良好的方式对方法进行分组,这样您就不会有太多的辅助类/接口,也不必向特定的Handler类注入太多。

在您的示例中,您将为Docs创建以下助手类:

internal class DocHelper
{
    internal virtual Doc GetDoc(int docId) 
    { 
        // ...
    }
    // Other methods as required
}

DocHandler和CloudHandler在其构造函数中获取一个实例,并使用该实例来获取文档,例如:

public class DocHandler : ChainHandler
{
    private readonly DocHelper docHelper; 
    public DocHandler(DocHelper docHelper)
    {
        Log = new LogWrapper(this); 
        this.docHelper = docHelper;
    }
    // ...
    public oAPI GetModel(int DocId)
    {
        var Model = docHelper.GetDoc();
        return RB(HTTPCodes.OK, data: Serializer(
        Model));
    }
    // ...
}

这种方法使您能够在测试处理程序时提供模拟助手类。