【问题标题】:Design pattern question for maintainability可维护性的设计模式问题
【发布时间】:2023-04-06 22:37:02
【问题描述】:

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

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

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
   }
}

现在每次添加新检查时,我都必须重构具体类以包含新检查。每个具体类将不同的属性传递给检查方法,因此子类化具体类不是一种选择。关于如何以更清洁的方式实现这一点的任何想法?

【问题讨论】:

  • 我个人认为您当前的方法非常干净。我们在谈论多少个具体的类?随着新检查的出现,调整必要的具体类真的很难吗?我认为不管你如何削减它,你最终还是会得到一些你必须调整的具体课程。避免这种情况的唯一方法是在具体类使用的某处执行“CheckAll()”样式函数。不过总体而言,我认为无需调整带来的性能提升不会超过泥浆因素。

标签: c# design-patterns


【解决方案1】:

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

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

【讨论】:

  • 是的,可以共享 IShouldPerformCheck 的单个实现。它实际上执行共享验证规则,例如验证 IP 未被阻止、电子邮件未被阻止或关键字未被阻止。对 IShouldPerformCheck 而言,重要的是输入特定于验证规则。具体的类可以以不同的方式提供这些输入。例如,关键字检查可能包括具体类的几个不同属性。
【解决方案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");
    }
}

【讨论】:

  • 谢谢杰菲。很好的解释。
【解决方案3】:

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

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

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

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

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

您可以考虑使用Predicate<T> 委托,而不是使用IShouldPerformActionCheck,它的作用大致相同。

【讨论】:

    【解决方案4】:

    当您创建一个新的CheckN 时,无论如何您都必须在每个具体的检查器类中实现它,不是吗?

    或者您是在谈论重构您的 IPerformActions 以实际调用该检查?

    你为什么不只是一个调用所有东西的CheckAll

    【讨论】:

    • 对 CheckN 的每次调用都将具有特定于调用它的具体类的数据。 CheckAll 方法可以工作,但它仍然需要具体类将其特定数据应用于它。我想得越多,我就越想真的没有办法防止每次添加都必须进入每个具体的课程。我想另一种选择是 ShouldPerformActionCheckerFactory 并在那里应用具体的类特定逻辑。不过,我不确定这是否会使水变得浑浊。
    • 如果您必须针对将哪些数据传递给哪个方法做出特定于类的决定,那么当您添加另一个检查时,无论您以哪种方式切片,都必须调整每个类它。
    • 不一定。请参阅我对双重调度的描述。
    • 如果您需要做出特定于类的决定,则需要在某处做出这些决定。
    【解决方案5】:

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

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

    要做到这一点可能需要你稍微重构一下,以便执行操作的逻辑包含在一个类中,而要使用的特定数据在另一个类中(有点像命令模式),这样跳棋可以做他们的工作。

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

    【讨论】:

      【解决方案6】:

      你可以试试这样的:

      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
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-29
        • 2011-02-21
        • 1970-01-01
        • 2011-03-07
        • 2011-04-03
        • 1970-01-01
        相关资源
        最近更新 更多