Как я могу реорганизовать это, чтобы работать без ужасного нарушения схемы? - PullRequest
2 голосов
/ 12 марта 2010

У меня есть объект базового класса, который используется для фильтрации. Это объект метода шаблона, который выглядит примерно так:

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. Если в оставшейся отфильтрованной коллекции содержится более 1 объекта, мы делаем с ней некоторые деловые вещи (неважно, что проблема здесь).
  4. SendToSuccessor(...) вызывается, если после фильтрации не было найдено достаточно объектов (это составной элемент, в котором следующий класс также будет получен из фильтра, но с другими критериями фильтрации)

Все было хорошо, но сейчас я создаю другой набор фильтров, который я собирался подклассить из этого. Однако фильтры, которые я создаю, потребуют разных параметров, и я не хочу просто реализовывать эти методы и не использовать параметры или просто добавлять в список параметров те, которые мне нужны, и не использовать их в существующих фильтрах.

Они по-прежнему выполняют один и тот же логический процесс.

Я также не хочу усложнять потребительский код для этого (который выглядит так)

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 ]

2 голосов
/ 12 марта 2010

Поскольку фильтр принимает другой параметр, и он всегда должен выполняться для набора данных и возвращать желаемый результат, почему вы не используете шаблон команды.

Имеют разные типы команд, каждый из которых принимает свой набор параметров, и всегда имеет метод Execute или Process, который вы вызываете, и всегда возвращает набор данных. Я предполагаю, что вы всегда имеете дело с фиксированным набором данных, в противном случае вы можете даже использовать generic и ограничить тип возвращаемого значения следующим образом:

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

     public IList<T> Execute();
     .........
  }
2 голосов
/ 12 марта 2010

Вы можете реализовать фильтры как Декораторы . Фактически, способ вызова преемника в вашем фильтре точно такой же, как у 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, ##);
    }
}

Как вы, вероятно, хорошо знаете, вы можете встроить свой метод шаблона в стратегию : один общий базовый интерфейс, абстрактный подкласс общего шаблона метода для каждого отдельного набора фильтров, а затем набор конкретных Подклассы реализации для каждого.

Трудной частью является другой набор параметров. Если вы хотите использовать новые фильтры, прозрачно смешанные со старыми, они должны иметь один и тот же интерфейс, таким образом, они все должны получить (и передать) расширенный набор всех существующих параметров, который быстро становится уродливым за счет 4- 5 параметров.

Можно было бы передать специальные параметры в конструкторе каждого фильтра и общие параметры process, но это сложно, поскольку у вас много циклов в цикле с различными параметрами ... Если вы не можете двигаться весь вложенный цикл foreach внутри фильтров, который затем получит users и regions в качестве параметров:

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 голос
/ 12 марта 2010

Звучит так, как будто вы хотите, чтобы ваш звонок был 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
    }
    ...
}
0 голосов
/ 12 марта 2010

Я бы подумал о небольшом рефакторинге, чтобы использовать (если возможно) шаблон перехватывающего фильтра.

Если это не сработает, потому что вы не можете придумать общий интерфейс для всех фильтров, вы можете использовать шаблон Decorator вокруг Front Controller (, как описано здесь ) или даже применить шаблон Цепочки ответственности, который является очень абстрактным.

0 голосов
/ 12 марта 2010

Почему вы не хотите определять интерфейс IFilter с одним методом void Process(User u, GeoRegion r, int countNeeded), а затем абстрактный класс FilterType1 (с абстрактными методами Select и Filter) с двумя производными классами и другим классом Filter3 (просто реализует метод Process)?

0 голосов
/ 12 марта 2010

Можете ли вы передать части, которые идут на фильтрацию, как коллекцию params ? Оставьте все остальное таким же, но у вас есть набор возможных параметров фильтра, с которыми каждый подкласс знает, как обращаться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...