Вопрос шаблона проектирования для удобства обслуживания - PullRequest
6 голосов
/ 16 декабря 2009

Я не уверен, есть ли здесь шаблон, который следует использовать, но вот ситуация:

У меня есть несколько конкретных классов, которые реализуют интерфейс:

public interface IPerformAction
{
   bool ShouldPerformAction();
   void PerformAction();
}

У меня есть другой класс, который проверяет входные данные, чтобы определить, следует ли выполнять ShouldPerformAction. Суть в том, что новые чеки добавляются довольно часто. Интерфейс для класса проверки определяется следующим образом:

public interface IShouldPerformActionChecker
{
   bool CheckA(string a);
   bool CheckB(string b);
   bool CheckC(int c);
   // etc...
}

Наконец, в настоящее время у меня есть конкретные классы, вызывающие каждый из методов проверки с данными, специфичными для этого конкретного класса:

public class ConcreteClass : IPerformAction
{
   public IShouldPerformActionCheck ShouldPerformActionChecker { get; set; }

   public string Property1 { get; set; }
   public string Property2 { get; set; }
   public int Property3 { get; set; }

   public bool ShouldPerformAction()
   {
      return 
         ShouldPerformActionChecker.CheckA(this.Property1) ||
         ShouldPerformActionChecker.CheckB(this.Property2) ||
         ShouldPerformActionChecker.CheckC(this.Property3);
   }

   public void PerformAction()
   {
      // do something class specific
   }
}

Теперь каждый раз, когда я добавляю новую проверку, я должен рефакторинг конкретных классов, чтобы включить новую проверку. Каждый конкретный класс передает различные свойства методу проверки, поэтому подклассы конкретных классов не являются опцией. Любые идеи о том, как это может быть реализовано в более чистой форме?

Ответы [ 6 ]

3 голосов
/ 16 декабря 2009

Давайте сделаем шаг назад - почему вы используете интерфейсы в первую очередь? Можно ли совместно использовать одну реализацию IShouldPerformActionCheck между несколькими реализациями IPerformAction? Кажется, ответ - нет, так как ICheck должен знать о свойствах реализации (Property1, Property2, Property3) в действии, чтобы выполнить проверку. Следовательно, взаимосвязь между IAction и ICheck требует больше информации, чем договор ICation может предоставить ICheck. Кажется, что ваши классы Check должны основываться на конкретных реализациях, которые связаны с конкретным типом проверяемого действия, например:

abstract class CheckConcreteClass
{
    abstract void Check(ConcreteClass concreteInstance);
}
2 голосов
/ 16 декабря 2009

Имена "CheckA", "CheckB" и т. Д., Предположительно выбранные, чтобы избежать раскрытия конфиденциальной информации, также запутывают природу отношений между классами, поэтому мне придётся ее использовать.

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

РЕДАКТИРОВАТЬ: попробуйте сыграть шаблон двойной отправки "по книге", а не разложить объекты в середине диспетчеризации. Для этого вам понадобится что-то вроде следующего:

public interface IPerformAction
{
    bool ShouldPerformAction(IShouldPerformActionChecker checker);
    void PerformAction();
}

public interface IShouldPerformActionChecker
{
    bool CheckShouldPerformAction(FloorWax toCheck);
    bool CheckShouldPerformAction(DessertTopping toCheck);
    // etc...
}

public class FloorWax : IPerformAction
{
    public string Fragrance { get; set; }

    // Note that the text of this method is identical in each concrete class,
    // but compiles to call a different overload of CheckShouldPerformAction.
    public bool ShouldPerformAction(IShouldPerformActionChecker checker)
    {
        return checker.CheckShouldPerformAction(this);
    }
}

public class DessertTopping: IPerformAction
{
    public string Flavor { get; set; }

    // Note that the text of this method is identical in each concrete class,
    // but compiles to call a different overload of CheckShouldPerformAction.
    public bool ShouldPerformAction(IShouldPerformActionChecker checker)
    {
        return checker.CheckShouldPerformAction(this);
    }
}

public class ShimmerApplicationChecker : IShouldPerformActionChecker
{
    // handles binding of FloorWax class to required checks
    public bool CheckShouldPerformAction(FloorWax toCheck)
    {
        return CheckAroma(toCheck.Fragrance);
    }

    // handles binding of DessertTopping class to required checks
    public bool CheckShouldPerformAction(DessertTopping toCheck);
    {
        return CheckAroma(toCheck.Flavor);
    }

    // some concrete check
    private bool CheckAroma(string aroma)
    {
        return aroma.Contains("chocolate");
    }
}
1 голос
/ 16 декабря 2009

Вы можете объединить проверки в единый общий метод, который принимает объект:

public interface IShouldPerformActionChecker
{
   bool Check(object o);
}

Затем составьте список этих проверок в вашем конкретном классе:

public List<IShouldPerformActionCheck> ShouldPerformActionChecker { get; set; }

Менее безопасный тип, но более гибкий.

Вместо использования IShouldPerformActionCheck вы можете посмотреть на использование Predicate делегата, что примерно так же.

0 голосов
/ 16 декабря 2009

Вы можете попробовать что-то вроде этого:

List<IShouldPerformActionChecker> checkers = new List<IShouldPerformActionChecker>();

//... add in each checker to try

foreach(ConcreteClass objectToCheck in checkset) {
   bool isGood = true;
   foreach(IShouldPerformActionChecker checker in checkers) {
      isGood &= checker.DoCheck(objectToCheck.Property);

       if (!isGood) { break; }
   }

   //handle failed check here
}
0 голосов
/ 16 декабря 2009

Вместо того, чтобы конкретные классы пытались проверить, должно ли быть выполнено действие, может быть более удобный способ упорядочить эти объекты.

Что, если бы программа проверки фактически реализовала IPerformAction и имела элемент IPerformAction, который он вызвал бы, если действие должно быть выполнено? Этот член может быть либо другим средством проверки в цепочке, либо фактическим классом, выполняющим действие, если все критерии были выполнены?

Для этого может потребоваться незначительный рефакторинг, чтобы логика выполнения действия содержалась в одном классе, а конкретные данные, которые нужно использовать, - в другом (что-то вроде шаблона Command), так что шашки могли выполнять свою работу.

Таким образом, вы можете легко добавить другое правило проверки, просто поместив его в «цепочку» объектов, ведущих к последнему действию.

0 голосов
/ 16 декабря 2009

Когда вы создаете новый CheckN, вам все равно придется реализовать его в каждом из ваших конкретных классов проверки, не так ли?

Или вы говорите о рефакторинге ваших IPerformActions для фактического вызова этой проверки?

Почему у вас просто нет CheckAll, который звонит всем?

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