如何在不破坏模式的情况下重构这个工作呢?

时间:2010-03-11 22:34:32

标签: c# .net design-patterns

我有一个用于过滤的基类对象。它是一个模板方法对象,看起来像这样。

public class Filter
{
    public void Process(User u, GeoRegion r, int countNeeded)
    {
        List<account> selected = this.Select(u, r, countNeeded); // 1
        List<account> filtered = this.Filter(selected, u, r, countNeeded); // 2

        if (filtered.Count > 0) { /* do businessy stuff */ } // 3

        if (filtered.Count < countNeeded)
            this.SendToSuccessor(u, r, countNeeded - filtered) // 4
    }
}

Select(...)Filter(...)是受保护的抽象方法,由派生类实现。

  1. Select(...)根据x条件查找对象
  2. Filter(...)过滤那些进一步选择的内容。
  3. 如果剩下的过滤集合中有多个对象,我们会用它做一些业务(这里的问题不重要)。
  4. 如果在过滤后找不到足够的对象,则会调用
  5. SendToSuccessor(...)(它是一个复合,其中下一个连续类也将从Filter派生但具有不同的过滤条件)
  6. 一切都没问题,但现在我正在构建另一组过滤器,我将从中继承这个过滤器。我正在构建的过滤器需要不同的参数,我不想只实现这些方法而不使用参数,或者只是将参数列表添加到我需要的参数列表中,并且不在现有的过滤器中使用它们。

    他们仍然执行相同的逻辑过程。

    我也不想让消费者代码复杂化(看起来像这样)

    Filter f = new Filter1();
    Filter f2 = new Filter2();
    Filter f3 = new Filter3();
    
    f.Sucessor = f2;
    f2.Sucessor = f3;
    /* and so on adding filters as successors to previous ones */
    
    foreach (User u in users)
    {
        foreach (GeoRegion r in regions)
        {
            f.Process(u, r, ##);
        }
    }
    

    我应该怎么做?

6 个答案:

答案 0 :(得分:2)

您可以将过滤器实现为Decorators。事实上,在过滤器中调用后继者的方式与Decorator完全相同,因此为什么不完全实现模式。主要区别在于后继在构造函数中传递。然后你就可以把它们连在一起:

Filter f3 = new Filter3();
Filter f2 = new Filter2(f3);
Filter f = new Filter1(f2);

foreach (User u in users)
{
    foreach (GeoRegion r in regions)
    {
        f.Process(u, r, ##);
    }
}

您可能已经清楚了解,可以将模板方法嵌入到Strategy:一个公共基本接口,每个不同过滤器集的抽象通用模板方法子类,然后是一组具体实现子类为每个人。

困难的部分是不同的参数集。如果你想使用与旧的过滤器透明混合的新过滤器,它们应该具有相同的接口,因此它们都应该获得(并向前传递)所有现有参数的超集,这在4的计数中很快变得丑陋5个参数。

一种可能性是将每个过滤器的构造函数中的特殊参数和公共参数传递给process,但这很难,因为在具有不同参数的循环中有很多运行...除非你可以在筛选器中移动整个嵌套的foreach循环,然后将usersregions作为参数:

public class Filter
{
    private UserCollection users;
    private GeoRegionCollection regions;

    public void Process(UserCollection users, GeoRegionCollection regions)
    {
        this.users = users;;
        this.regions = regions;
    }

    public void Process()
    {
        foreach (User u in users)
        {
            foreach (GeoRegion r in regions)
            {
                Process(u, r, ##);
            }
        }
    }

    public void Process(User u, GeoRegion r, int countNeeded)
    {
        // as before
    }
}

另一种可能性是将这些参数组合在一起,这些参数具有足够的可变性来保存您的不同参数集。通常情况下,这将是一些丑陋的地图式存储,您需要按名称查找参数,然后将它们向下转换为正确的类型: - (

如果不了解更多细节,很难说更多......

答案 1 :(得分:2)

由于过滤器接受不同的参数,并且它总是必须对一组数据执行并返回所需的结果,为什么不使用命令模式。

具有不同的命令类型,每个命令类型都接受一组不同的参数,并始终具有您调用它的方法Execute或Process,并始终返回一组数据。我想你总是处理一组固定的数据,否则你甚至可以使用泛型并约束返回类型,如下所示:

  public class GregorianFilterCommand<T>: FilterCommand where T is IBusinessEntity
  {
     public GregorianFilterCommand(IList<T> rawDataList, .....);

     public IList<T> Execute();
     .........
  }

答案 2 :(得分:1)

听起来你想做的就是让电话成为this.Filter(selected, u, r, countNeeded, more, parameters, here);。如果是这样,您可以将附加参数实现为构造函数的参数,这些参数存储为私有成员字段,如下所示:

class SuperFilter : Filter
{
    private object more, parameters, here;
    public SuperFilter(object more, object parameters, object here)
    {
        this.more = more;
        this.parameters = parameters;
        this.here = here;
    }
    override protected List<account> Filter(selected, u, r, countNeeded)
    {
        // use more parameters here along with the regular parameters
    }
    ...
}

答案 3 :(得分:0)

你能否将过滤的作品作为params集合传递?保持其他所有内容相同,但有一组可能的过滤器参数,每个子类都知道如何处理。

答案 4 :(得分:0)

为什么不想用一个方法IFilter定义接口void Process(User u, GeoRegion r, int countNeeded)然后抽象类FilterType1(使用抽象方法SelectFilter)有两个派生类和另一个类Filter3(只是实现Process方法)?

答案 5 :(得分:0)

我会考虑使用一个小的重构(如果可能的话)拦截过滤器模式。

如果这不起作用,因为您无法为所有过滤器提供通用接口,您可以在前端控制器(as described here)周围使用装饰器模式,甚至应用责任链模式,非常抽象。