我的公司处理纸质表单,以便将书面数据放入数据库。每个表单都有一组字段。我正在编写一个简单的内部应用程序来定义表单的字段,其中包括字段的验证。我现在正在考虑课程结构,我可以想到两种方法:
我可以编写一组类,每个类代表一次验证,例如 RegexValidation 及其 Pattern 属性, LengthValidation ,其 Min 和 Max 等等。但是,如果将来出现任何新的验证类型,我可能会在项目中有很多地方需要编写新代码。我真的不认为会有任何新的验证类型,但这是一个我不应该作为程序员的假设。
第二种方法是创建一个抽象类 Validation ,所有验证器类都将继承它。他们将有一个字典,将参数名称映射到它们的值(这样 LengthValidation 将包含键“ max ”和“ min ”的项目和 RegexValidation 会包含带有“模式”等字符的项目。它看起来很不错,但是存在一个主要问题 - 当将数据写入数据库时,我必须知道哪个验证是哪一个,以便我可以将值放在适当的位置。我可以使用策略设计模式,让Validation类具有Save方法,以便每个类都知道它在数据库中的保存方式。但另一方面,我不希望验证类负责将数据写入数据库。
所以你会建议什么?还有其他想法吗?
答案 0 :(得分:1)
编写类层次结构很好。验证有许多验证子类。
当出现新的验证时,不需要重写任何旧的验证。旧的东西仍然存在,仍然有效。当有人要求新的SomeNewIDNumberValidation时,EmailAddressValidation不会更改。
如果你发现了一个错误,当然会重写一个类。
当你添加一个新的验证时,你不会“在项目中有很多地方我必须编写新的代码”。您将获得新验证和需要新验证的应用程序。什么都没有。这是OO软件的美妙之处 - 添加子类不会破坏任何内容。
您需要所有验证器子类都有一个统一的“is_valid”方法。这就是你如何使它们变成多态的。它们不需要具有相同的构造函数,只需要相同的验证器。
此外,您需要每个验证程序对象返回“已清理,可供数据库使用”值。一些验证者可以(并且应该)清理他们的输入。从信用卡号码中删除空格。例如,删除任何插入电话号码的奇怪标点符号。
您想构建一个包含许多验证器的复合对象。我将使用Python语法,因为它更简单。
class SomeValidatableObject( object ):
field1 = ThisValidator( size=12, otherSetting="B" )
field2 = RegexValidator( expr=r"\d{3}-\d{2}" )
field3 = SSNValidator()
field4 = YetAnotherValidator( someSetting=9, size=14 )
所有构造函数都特定于验证。所有逐场验证都是常见的is_valid
方法。每个验证器都可以使用clean_data
方法。
您的复合对象可以使用save
方法从所有各个字段的值构建生成的有效对象。
[我没有设计这个,我正在描述Django Project使用的验证器。]如果你查看Forms文档,你会看到他们如何解决这个问题。 / p>
答案 1 :(得分:0)
grails有一堆验证器:http://www.grails.org/Validation+Reference。考虑构建验证器层次结构并使用从成员名称到验证器的静态映射。当违反约束时,您可能想要考虑错误消息的i18n。
答案 2 :(得分:0)
我不知道这是否完全符合你的问题但是......
我最近使用过Microsofts模式和实践验证应用程序块 - 我真的很喜欢实现http://msdn.microsoft.com/en-us/library/cc309509.aspx
也许如果你看看他们提供什么,它会给你一些想法。
答案 3 :(得分:0)
在几个带有验证逻辑的项目之后,我来到了第三个选项。
通用规则定义为:
///<summary>
/// Typed delegate for holding the validation logics
///</summary>
///<param name="obj">Object to validate</param>
///<param name="scope">Scope that will hold all validation results</param>
///<typeparam name="T">type of the item to validate</typeparam>
public delegate void Rule<T>(T obj, IScope scope);
IScope 是
/// <summary>
/// Concept from the xLim2. That's simple nesting logger that is used by
/// the validation rules.
/// </summary>
public interface IScope : IDisposable
{
/// <summary>
/// Creates the nested scope with the specified name.
/// </summary>
/// <param name="name">New name for the nested scope.</param>
/// <returns>Nested (and linked) scope instance</returns>
IScope Create(string name);
/// <summary>
/// Writes <paramref name="message"/> with the specified
/// <paramref name="level"/> to the <see cref="IScope"/>
/// </summary>
/// <param name="level">The level.</param>
/// <param name="message">The message.</param>
void Write(RuleLevel level, string message);
/// <summary>
/// Gets the current <see cref="RuleLevel"/> of this scope
/// </summary>
/// <value>The level.</value>
RuleLevel Level { get; }
}
和规则级别是:
/// <summary>
/// Levels leveraged by the <see cref="Rule{T}"/> implementations
/// </summary>
public enum RuleLevel
{
/// <summary> Default value for the purposes of good citizenship</summary>
None = 0,
/// <summary> The rule raises a warning </summary>
Warn,
/// <summary> The rule raises an error </summary>
Error
}
即使没有声明新类,您也可以定义新规则:
public static void ValidEmail(string email, IScope scope)
{
if (!_emailRegex.IsMatch(email))
scope.Error("String is not a valid email address");
}
或通过附件组成新的验证器
/// <summary>
/// Composes the string validator ensuring string length is shorter than
/// <paramref name="maxLength"/>
/// </summary>
/// <param name="maxLength">Max string length.</param>
/// <returns>new validator instance</returns>
public static Rule<string> Limited(int maxLength)
{
Enforce.Argument(() => maxLength, Is.GreaterThan(0));
return (s, scope) =>
{
if (s.Length > maxLength)
scope.Error("String length can not be greater than '{0}'", maxLength);
};
}
规则可以组合在一起并保持可读性:
internal static Rule<string>[] UserName = new[]
{
StringIs.Limited(6, 256),
StringIs.ValidEmail
};
为了运行规则,您只需传递一个对象,一个范围(将输出写入),然后检查范围以获得结果。
此范例中的其他可扩展性点:
您可以通过传入不同的范围实现来实现不同的规则行为(不更改规则)。例如:
在这个简单的概念中有更多的可扩展性点))
BTW,Open Source implementation适用于.NET。
PS:在我们公司,我们已经在单个文件中定义了一些公共API规则(相当小),并在各处重用这些规则进行验证。