创建多个(15+)HTTP响应过滤器,继承与组合w /注入

时间:2010-12-13 20:52:01

标签: unit-testing inheritance architecture dependency-injection composition

首先回顾一下我想要完成的事情。

我正在创建一个自定义HTTP模块,其目的是截取多个(15+)个不同ArcGIS REST Web服务的消息。截获的请求和/或响应将根据当前用户被删除任何受限信息。

例如,返回多个图层的调用可能会删除某些图层。

未经修改的回复:

"layers" : [
    {
      "id" : 0, 
      "name" : "Facilities", 
      "parentLayerId" : -1, 
      "defaultVisibility" : true, 
      "subLayerIds" : [1, 2, 3]
    }, 
    {
      "id" : 1, 
      "name" : "Hazardous Sites", 
      "parentLayerId" : 0, 
      "defaultVisibility" : true, 
      "subLayerIds" : null
    }, 
]

修改后的回复:

"layers" : [
    {
      "id" : 0, 
      "name" : "Facilities", 
      "parentLayerId" : -1, 
      "defaultVisibility" : true, 
      "subLayerIds" : [1, 2, 3]
    }
]

有许多可用的服务,所有服务都通过URL唯一标识。每个服务返回非常不同的信息,因此需要进行不同的过滤。此外,每个服务都可以以各种格式(HTML,JSON等)返回数据。

因此,我需要创建大量不同的过滤器以应用于HttpRequest.Filters和/或HttpResponse.Filters。

示例:

// Request for layers and the format is JSON
IPolicy policy = GetPolicy(userContext);
Filter filter = new LayerJsonResponseFilter(Response.Filter, policy);
Response.Filter = filter;

请求和响应过滤器是通过继承Stream(或从Stream继承的另一个类,如MemoryStream)实现的。我希望能够轻松创建新的过滤器,而无需为每个过滤器重新实现Stream。

此处描述了一个潜在的解决方案:http://www.west-wind.com/weblog/posts/72596.aspx

但是,我希望简化解决方案,而不会失去指定许多不同转换的灵活性,而无需重新实现流。我认为我可以通过以下方式实现这一目标:

  1. 从MemoryStream继承,以减少方法的重新实现。
  2. 始终使用完整内容,而不是分块内容。
  3. 使用抽象方法替换事件(例如,过滤器())
  4. 我考虑了两种可能的解决方案。

    解决方案1:创建从ResponseFilter继承的多个过滤器

    在这种情况下,每个过滤器都包含执行过滤的逻辑。将创建15个以上的过滤器,所有过滤器都继承自通用的ResponseFilter抽象基类,如下所示:

    // All filters will inherit from ResponseFilter
    public abstract class ResponseFilter : MemoryStream
    {
        public ResponseFilter(Stream stream, Policy policy) { }
    
        // Must be overridden in a derived class with specific Filter logic.
        public abstract string Filter(string content);
    
        // Overridden to cache content.    
        public override void Write(byte[] buffer, int offset, int count) { }
    
        // Overridden to perform the filter/transformation before the content is written.
        public override void Flush()
        {
             // Get stream content as a string
    
             string content = Filter(content);
    
             // Write new content to stream
        }
    }
    

    这将按以下方式使用。

    // Example
    var policy = GetPolicy();
    var filter = new MapServiceJsonResponseFilter(response.Filter, policy);
    response.Filter = filter;
    

    此选项的优点是类的数量保持最小。但是,如果必要,在应用程序中的任何其他位置重用任何过滤器逻辑变得很困难。此外,单元测试过滤器需要模拟Stream,这是另一个缺点。

    解决方案2:创建多个过滤器,注入公共ResponseFilter

    在此方案中,将创建单个响应过滤器。实际的滤波器逻辑或算法被注入滤波器。所有过滤器都继承自抽象基类FilterBase。

    // Represents an HttpResponse Filter. Renamed to avoid confusion with
    // the filter algorithm.
    public class ResponseFilterStream : MemoryStream
    {
        public ResponseFilterStream(Stream stream, FilterBase filter) { }
    
        // Overridden to cache content.    
        public override void Write(byte[] buffer, int offset, int count) { }
    
        // Overridden to perform the filter/transformation before the content is written.
        public override void Flush()
        {
             // Get stream content as a string
    
             string content = _filter.Filter(content);
    
             // Write new content to stream
        }
    }
    
    // All filter algorithms inherit from FilterBase and must implement 
    // the filter method.
    public abstract class FilterBase
    {
        protected TransformBase(Policy policy) { }
    
        // Overridden to perform the filter/transformation.    
        public abstract string Filter(string content);
    }
    

    这将按以下方式使用。

    // Example
    var policy = GetPolicy();
    var filter = new MapServiceJsonResponseFilter(policy);
    ResponseFilter responseFilter = new ResponseFilter(response.Filter, filter);
    response.Filter = filter;
    

    此解决方案的优点是过滤逻辑完全独立于实现流的任何类。如有必要,可以更容易地重用逻辑。单元测试稍微简单一些,我不需要模拟流。

    但是,有更多的类(正好为1),使用情况稍微复杂一些,但并非如此。

    注意:我可能想重命名FilterBase,以避免与ResponseFilter混淆。也许是TransformBase。


    我有几个目标,我想要解决这两个问题。

    1. 解决方案必须是高度可测试的。单元测试将用于检查过滤器的正确性。测试必须尽可能简单。
    2. 解决方案必须能够轻松支持创建多个过滤器(15 +)。
    3. 解决方案应该是可读的(即易于维护)。
    4. 我认为解决方案2是针对此特定方案的最佳解决方案。我可以完全独立于Stream测试过滤逻辑,而且复杂性最小。两种解决方案都支持#2和#3,因此测试可以获得优势。

      还有哪些其他考虑因素?还有更好的选择吗?

1 个答案:

答案 0 :(得分:2)

溶液2显然是优选的。然而,似乎问题的主要症结在于滤波器本身的构造。希望Filter实现中有很多可重用的组合。可以从复合零件“配置”新过滤器吗?