多个类具有相同的方法 - 最佳模式

9

在我的当前项目中,有一些类需要验证电子邮件/网站地址。实现这个功能的方法都相同。

我想知道最好的实现方式是什么,这样我就不需要把这些方法到处复制粘贴了。

这些类本身并不一定相关,它们只是具有这些验证方法的共同点。


这些类还有什么其他功能?为了清晰起见,一个类应该只有一个目的。FrustratedWithFormsDes的解决方案遵循了这个原则。 - outis
9个回答

17

加入一个接口,然后使用扩展方法怎么样?

public interface IFoo { }

public class A : IFoo {}
public class B : IFoo {}
public class C : IFoo {}

public static class FooUtils {
    public static void Bar(this IFoo foo) { /* impl */ }
}

这样做的好处:

  • 避免不必要的继承
  • 没有冗余代码

在A类内,我现在可以访问Bar方法吗? - Tony The Lion
是的,翻译如下: var aClass = new A(); aClass.Bar(); - sidney.andrews
2
看起来像是滥用扩展方法。如果它们可以只调用EmailUtils类的静态方法(或创建一个EmailValidator对象并调用其Validate方法),为什么要求所有需要电子邮件验证的类实现某些(空)接口并在其中执行一些扩展方法魔法?此外,似乎他只需要类内的电子邮件验证代码 - 这不应该需要实现接口。 - stmax
这绝对是接口和扩展方法的不良使用。令人惊讶的是,它获得了最多的投票并被标记为答案。 - mikesigs
这听起来像是一种“利用”来克服C#中缺乏多重类继承的问题。这在概念上可能听起来不正确,但结果与从“Foo”类继承并具有其自己实现的“Bar”类相同。问题出现在您还必须从另一个类继承时,这就是Marc解决方案的用武之地。 - Alex Bagnolini

9

您可能希望将所有验证代码放入一个名为Validator的类中,然后在需要进行验证的任何地方使用该类。访问验证应通过单个方法Validate(object Something)进行。我认为这被称为“组合”(就设计模式而言)。

稍后,您可以拥有更具体或执行不同类型验证的Validator子类。

您还可以使所有需要验证的类扩展一个具有90%验证内容的基类或抽象类。


1
最好的术语是“关联”,它是一种类关系(http://en.wikipedia.org/wiki/Class_diagram#Instance_Level_Relationships),而不是一种模式。至于模式,可能涉及策略模式(http://en.wikipedia.org/wiki/Strategy_pattern)。 - outis

6
听起来你只需要一个带有静态方法的静态类。
public static class Utilities{
    public static bool validEmail(string email)
    {
        //Your code here
    }
}

1
+1 实用类似乎是这里的最佳选择。创建一个实用类,其中包含验证电子邮件、检查网址和其他类型验证的静态方法。每当需要使用这些方法时,请调用此实用类的静态方法。 - Helen Neely

2

是的,复制这段代码会产生不好的代码气味。你可以将这些方法提取到一个单独的 Helper 类中作为静态方法,或者定义一个“验证器”接口类,并使用该接口将不同的验证方法与责任链模式链接起来。


1
创建一个实用类,并将这些方法定义为适当类/接口的扩展方法。

1
你真的需要认真研究面向切面编程方法论(AoP)。Enterprise Library 4.1具有名为Unity拦截(Unity Interception)的AoP实现。

http://msdn.microsoft.com/en-us/library/dd140045.aspx

这个框架允许您为电子邮件验证编写一个单一的处理程序类。这意味着验证代码放入处理程序类中,而不再是类的一部分。接下来要做的事情是标记要拦截的类。

您可以以多种方式拦截类,包括在所需方法上设置属性,以便根据您的要求进行拦截和处理。设置属性可能是执行拦截的最简单方法。


0
我建议您创建一个IValidator接口,然后创建多个处理不同场景的不同验证器。这里是一个例子:
public interface IValidator {
    bool CanValidateType(string type);
    bool Validate(string input);
}

CanValidateType() 方法可能会更加复杂,但我希望你能理解它的基本思想。它主要是识别验证器是否能够处理所提供的输入。以下是一些实现方式:

public class UrlValidator : IValidator {
    bool CanValidateType(string type) {
        return type.ToLower() == "url";
    }

    bool Validate(string input) {
        /* Validate Url */
    }
}

public class EmailValidator : IValidator {
    bool CanValidateType(string type) {
        return type.ToLower() == "email";
    }

    bool Validate(string input) {
        /* Validate Email */
    }
}

现在,您将使用构造函数注入将依赖项注入到您的类中:
public class SomeSimpleClass {
    private IValidator validator;

    public SomeComplexClass(IValidator validator) {
        this.validator = validator;
    }

    public void DoSomething(string url) {
        if (validator.CanValidateType("url") && 
            validator.Validate(url))
            /* Do something */
    }
}

当你有一个可以使用多个验证器的类时,CanValidateType非常方便。在这种情况下,您将验证器列表或数组传递给构造函数。
public class SomeComplexClass {
    private List<IValidator> validators;

    public SomeComplexClass (List<IValidator> validators) {
        this.validators = validators;
    }

    public bool ValidateUrl(string url) {
        foreach (IValidator validator in this.validators)
            if (validator.CanValidateType("url"))
                return validator.Validate(url);
        return false;
    }


    public bool ValidateEmail(string email) {
        foreach (IValidator validator in this.validators)
            if (validator.CanValidateType("email"))
                return validator.Validate(email);
        return false;
    }
}

然后,您将不得不以某种方式将所需的验证器实例传递给您的类。这通常使用IoC容器(如Castle Windsor)完成,或者自己动手。

IValidator emailValidator = new EmailValidator();
IValidator urlValidator = new UrlValidator();
SomeSimpleClass simple = new SomeSimpleClass(urlValidator);
SomeComplexClass complex = new SomeComplexClass(new List<IValidator> { emailValidator, urlValidator });

以上代码自己编写会变得很繁琐,这就是为什么IoC容器非常方便。使用IoC容器,您可以执行以下操作:
SomeSimpleClass simple = container.Resolve<SomeSimpleClass>();
SomeComplexClass complex = container.Resolve<SomeComplexClass();

所有接口的映射都在您的 app.config 或 web.config 中完成。

这里有一个关于依赖注入和 Castle Windsor IoC 容器的绝妙教程


0
创建验证逻辑类和接口,并将它们注入到您的代码中...这样可以将逻辑与验证分离,以便可以重复使用...

0
创建一个名为 Email 的独立类。
在你的类中,使用 Email 作为属性/参数/返回值,而不是 String
创建一个名为 EmailValidator 的类来验证字符串是否为电子邮件地址。
创建一个名为 EmailFactory 的类,当传递有效的电子邮件地址时返回 Email,否则返回 null。
(对于网站也做同样的操作)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接