Вопрос о шаблоне разработки для пригодности для обслуживания

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

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

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
задан bmancini 16 December 2009 в 01:12
поделиться

6 ответов

Имена «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");
    }
}
2
ответ дан 17 December 2019 в 02:29
поделиться

Let's take a step back - why are you using interfaces in the first place? Can a single implementation of IShouldPerformActionCheck be shared between multiple implementations of IPerformAction? It seems the answer is no, since ICheck must be aware of implementation-specific properties (Property1, Property2, Property3) on the Action in order to perform the check. Therefore the relationship between IAction and ICheck requires more information than the IAction contract can provide to ICheck. It seems your Check classes should be based on concrete implementations that are coupled to the specific type of action they check, like:

abstract class CheckConcreteClass
{
    abstract void Check(ConcreteClass concreteInstance);
}
3
ответ дан 17 December 2019 в 02:29
поделиться

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

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

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

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

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

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

1
ответ дан 17 December 2019 в 02:29
поделиться

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

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

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

0
ответ дан 17 December 2019 в 02:29
поделиться

Instead of have the concrete classes try to check if the action should be performed, there might be a more maintainable way to have arrange these objects.

What if the checker actually implemented IPerformAction, and had an IPerformAction member that it would call if the action should be performed? That member could either be another checker in the chain, or the actual class that performs the action if all the criteria have been passed?

To do that might require that you refactor slightly, so that the logic to do the action is contained in one class, while the specific data to be used is in another one (kind of like a Command pattern), so that the checkers could do their jobs.

In this way, you could easily add another validation rule, just by putting it into the 'chain' of objects leading to the final action.

0
ответ дан 17 December 2019 в 02:29
поделиться

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

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
ответ дан 17 December 2019 в 02:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: