2009-12-16 88 views
6

设计模式的问题,我不知道是否有应该在这里使用的模式,但这里的情况是:可维护性

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

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

我有另一个类来检查输入,以确定是否应该执行ShouldPerformAction。蹭的是,新的检查相当频繁地加入。为检查类的接口定义如下:每次

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

现在:

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

最后我现在有具体的类调用每个特定于该具体类的检查方法与数据我添加一个新的支票,我必须重新构造具体的类以包含新的支票。每个具体类都将不同的属性传递给检查方法,所以具体类的子类不是一个选项。关于如何以更清洁的方式实施这些想法的任何想法?

+0

我个人认为,当前的方法是很干净。我们在讲多少课?新的支票进来后,真的很难调整必要的具体课程吗?我认为不管你如何削减它,你仍然会得到一些你必须调整的具体课程。避免这种情况的唯一方法是在具体类使用的某处执行“CheckAll()”样式函数。总体而言,我认为无需调整的性能提高不会超过泥土因素。 – mike 2009-12-16 03:22:45

回答

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"); 
    } 
} 
+0

谢谢Jeffey。很好的解释。 – bmancini 2009-12-16 15:38:15

0

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

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

为什么不只是有一个叫做一切的CheckAll

+0

对CheckN的每次调用都将具有特定于调用它的具体类的数据。 CheckAll方法可以工作,但它仍然需要具体的类将其特定的数据应用到它。考虑得越多,我就越想越多,实际上没有办法阻止每次增加都要进入具体课程。 我想一个替代方案是ShouldPerformActionCheckerFactory,并在那里应用具体的类特定逻辑。我不确定那是不是只是混浊了水。 – bmancini 2009-12-16 01:24:36

+0

如果您必须针对哪个数据传递给哪个方法做出特定于类的决定,那么无论您使用哪种方式对其进行分片,您都必须在添加其他校验时调整每个类。 – 2009-12-16 01:39:37

+0

不一定。看到我的双重调度的描述。 – 2009-12-16 02:20:48

1

你可以巩固支票兑换成接受一个对象的单个一般方法:

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

然后在你的具体类这些检查列表:

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

以下类型安全,但更灵活。

而不是使用IShouldPerformActionCheck你可以看看使用Predicate<T>委托,这大致相同的事情。

3

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

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

是的,IShouldPerformCheck的单个实现可以共享。它实际上执行共享验证规则,例如验证IP未被阻止,电子邮件未被阻止,或者该关键字未被阻止。所有对IShouldPerformCheck重要的是,输入是特定于验证规则的。具体的类可以以不同的方式提供这些输入。例如,关键字检查可能包括具体类的几个不同的属性。 – bmancini 2009-12-16 01:34:06

0

而是有具体的类尝试检查是否应执行的动作,有可能成为安排这些对象的更可维护的方式。

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

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

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

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 
}