设计模式问题的可维护性

时间:2009-12-16 01:12:16

标签: c# design-patterns

我不确定这里是否应该使用某种模式,但情况如下:

我有许多实现接口的具体类:

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 个答案:

答案 0 :(得分:3)

让我们退后一步 - 为什么你首先使用接口?可以在IShouldPerformActionCheck的多个实现之间共享IPerformAction的单个实现吗?似乎答案是否定的,因为ICheck必须知道Action上的特定于实现的属性(Property1,Property2,Property3)才能执行检查。因此,IAction和ICheck之间的关系需要比IAction合同可以提供给ICheck的更多信息。看来你的Check类应该基于与他们检查的特定动作类型相关的具体实现,例如:

abstract class CheckConcreteClass
{
    abstract void Check(ConcreteClass concreteInstance);
}

答案 1 :(得分:2)

“CheckA”,“CheckB”等名称,大概是为避免暴露机密信息而选择的,也会混淆类之间关系的性质,所以我不得不将它放在一边。

然而,这几乎是double dispatch,除了您正在执行中间对象的转换。

编辑:尝试“按书”播放双重调度模式,而不是在调度中分解对象。为此,您需要以下内容:

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 :(得分:1)

您可以将检查合并到一个采用对象的通用方法中:

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

然后在具体类中列出这些检查:

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

类型安全性较低,但更灵活。

您可以使用Predicate<T>委托,而不是使用IShouldPerformActionCheck,这与大致完全相同。

答案 3 :(得分:0)

当你创建一个新的CheckN时,你将不得不在每个具体的检查器类中实现它,不是吗?

或者您是在谈论重构IPerformActions以实际调用该支票?

为什么不只有一个CheckAll来调用一切?

答案 4 :(得分:0)

不是让具体类尝试检查是否应该执行操作,而是可能有一种更易于维护的方法来安排这些对象。

如果检查器实际实现了IPerformAction,并且有一个IPerformAction成员,如果应该执行该操作,它会怎么样?该成员可以是链中的另一个检查者,或者如果所有标准都已通过则执行操作的实际类?

要做到这一点,可能需要稍微重构,以便执行操作的逻辑包含在一个类中,而要使用的特定数据在另一个类中(类似于Command模式),以便跳棋可以完成他们的工作。

通过这种方式,您可以轻松添加另一个验证规则,只需将其放入导致最终操作的对象的“链”中即可。

答案 5 :(得分:0)

您可以尝试这样的事情:

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
}