在责任链模式中使用共享组件的最佳方式
Best way to use shared components in Chain of Responsibility pattern
我对责任链模式有疑问。
所有处理程序都实现了这个 class:
/// <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);
}
}
我的问题是找到在处理程序之间共享方法和属性的最佳方式。
现在我使用静态 class SharedComponents,其中每个处理程序委托自己的方法。
public static class SharedComponents
{
public static Func<int, Doc> GetDoc;
}
在 DocHandler 中,我委托方法 SharedComponents.GetDoc = this.GetDoc;
,然后在 CloudHandler var Model = SharedComponents.GetDoc(docid)
中使用它。这是将委托写入数百个共享方法的意大利面条。
但是我是如何测试的呢?我将必须初始化 all 处理程序(因为 A
使用 B
的方法,而 B
可能使用 C
等的方法)以在一个处理程序中测试 one 方法。恐怖!
我尝试将共享方法设置为静态,以便像 var Model = DocHandler .GetDoc(docid)
一样使用。但是这个解决方案打破了依赖倒置原则。如果某些共享方法使用上下文(如 UnitOfWork
中的 session
),我需要在测试中再次初始化所有处理程序!
应用责任链模式的一个主要目的是让一些步骤相互独立。目标是能够更改步骤的顺序或添加新步骤而无需更改现有步骤。
在您的方法中,即使您在共享 class 上使用委托,也会引入从 CloudHandler
到 DocHandler
的强依赖性,因为它只能 运行 如果委托被设置。
为了解决这个问题,我建议从处理程序中提取共享方法,并将它们移动到一个或多个具有虚拟方法或接口的助手 classes。您不是在处理程序中创建 class 的实例,而是在构造期间将所需的实例注入处理程序。您提到了数百种共享方法。以一种好的方式对方法进行分组很重要,这样您就不会有太多的帮助程序 classes/interfaces,也不必将太多的方法注入到特定的处理程序中 class。
在您的示例中,您将为 Docs 创建以下助手 class:
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));
}
// ...
}
此方法使您能够在测试处理程序时提供模拟助手 classes。
我对责任链模式有疑问。 所有处理程序都实现了这个 class:
/// <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);
}
}
我的问题是找到在处理程序之间共享方法和属性的最佳方式。 现在我使用静态 class SharedComponents,其中每个处理程序委托自己的方法。
public static class SharedComponents
{
public static Func<int, Doc> GetDoc;
}
在 DocHandler 中,我委托方法 SharedComponents.GetDoc = this.GetDoc;
,然后在 CloudHandler var Model = SharedComponents.GetDoc(docid)
中使用它。这是将委托写入数百个共享方法的意大利面条。
但是我是如何测试的呢?我将必须初始化 all 处理程序(因为 A
使用 B
的方法,而 B
可能使用 C
等的方法)以在一个处理程序中测试 one 方法。恐怖!
我尝试将共享方法设置为静态,以便像 var Model = DocHandler .GetDoc(docid)
一样使用。但是这个解决方案打破了依赖倒置原则。如果某些共享方法使用上下文(如 UnitOfWork
中的 session
),我需要在测试中再次初始化所有处理程序!
应用责任链模式的一个主要目的是让一些步骤相互独立。目标是能够更改步骤的顺序或添加新步骤而无需更改现有步骤。
在您的方法中,即使您在共享 class 上使用委托,也会引入从 CloudHandler
到 DocHandler
的强依赖性,因为它只能 运行 如果委托被设置。
为了解决这个问题,我建议从处理程序中提取共享方法,并将它们移动到一个或多个具有虚拟方法或接口的助手 classes。您不是在处理程序中创建 class 的实例,而是在构造期间将所需的实例注入处理程序。您提到了数百种共享方法。以一种好的方式对方法进行分组很重要,这样您就不会有太多的帮助程序 classes/interfaces,也不必将太多的方法注入到特定的处理程序中 class。
在您的示例中,您将为 Docs 创建以下助手 class:
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));
}
// ...
}
此方法使您能够在测试处理程序时提供模拟助手 classes。