You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于字典查找的策略模式实现验证引擎的优化问询

验证引擎策略模式的合理性分析与优化方案

首先得说,你当前用策略模式+Lazy延迟加载的方案整体是合理的:Lazy加载避免了一次性创建所有验证器实例,内存控制做得很到位;策略模式也完美解耦了不同属性的验证逻辑,每个验证器只负责单一职责,完全符合开闭原则。但问题也很明显——手动维护35+个属性与验证器的映射会非常繁琐,后期属性名变更、验证策略调整时,很容易漏改或出错,维护成本太高。

下面给你几个更优的实现思路,解决手动映射的痛点:

方案1:基于特性(Attribute)的配置驱动(最推荐)

通过给输入对象的属性打特性标记,自动扫描并构建验证器映射,彻底告别手动维护字典。

步骤1:定义验证器特性

先创建一个特性类,用来标记属性对应的验证器类型:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class ValidatorAttribute : Attribute
{
    public Type ValidatorType { get; }

    public ValidatorAttribute(Type validatorType)
    {
        if (!typeof(IValidationEngine).IsAssignableFrom(validatorType))
            throw new ArgumentException("Validator type must implement IValidationEngine");
        ValidatorType = validatorType;
    }
}

步骤2:给输入对象的属性打标记

假设你的载荷是强类型类(比如Payload),直接在属性上标记对应的验证器:

public class Payload
{
    [Validator(typeof(MandatoryStringLengthValidator))]
    public string Position { get; set; }

    [Validator(typeof(StringLengthValidator))]
    public string Pcode { get; set; }

    [Validator(typeof(MandatoryStringLengthValidator))]
    public string Username { get; set; }

    // 其他35个属性依次打标记即可
}

步骤3:改造ValidationRouter自动构建映射

通过反射扫描Payload类的属性,读取特性自动生成Lazy验证器字典:

public class ValidationRouter
{
    private readonly Dictionary<string, Lazy<IValidationEngine>> _strategies = new();

    public ValidationRouter()
    {
        // 扫描Payload类的所有公共实例属性
        foreach (var prop in typeof(Payload).GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            var validatorAttr = prop.GetCustomAttribute<ValidatorAttribute>();
            if (validatorAttr == null) continue;

            // 用Lazy延迟创建验证器实例
            var lazyValidator = new Lazy<IValidationEngine>(() => 
                (IValidationEngine)Activator.CreateInstance(validatorAttr.ValidatorType));
            
            // 保持属性名转小写的逻辑和之前一致
            _strategies.Add(prop.Name.ToLower(), lazyValidator);
        }
    }

    public Error Execute(string name, dynamic value)
    {
        var loweredName = name.ToLower();
        if (_strategies.TryGetValue(loweredName, out var validator))
        {
            return validator.Value.Validate(name, value);
        }
        return default(Error);
    }
}

后续新增属性只需要给属性打特性,完全不用修改ValidationRouter,维护成本直接降为0。

方案2:配置文件驱动(适合动态调整场景)

如果需要在不修改代码的情况下调整验证策略,可以用配置文件(比如appsettings.json)定义映射关系:

{
  "ValidationMappings": {
    "position": "YourNamespace.MandatoryStringLengthValidator",
    "pcode": "YourNamespace.StringLengthValidator",
    "username": "YourNamespace.MandatoryStringLengthValidator",
    // 其他属性映射...
  }
}

然后在ValidationRouter中读取配置构建字典:

public class ValidationRouter
{
    private readonly Dictionary<string, Lazy<IValidationEngine>> _strategies = new();

    public ValidationRouter(IConfiguration configuration)
    {
        var mappings = configuration.GetSection("ValidationMappings").Get<Dictionary<string, string>>();
        if (mappings == null) return;

        foreach (var (propName, validatorTypeName) in mappings)
        {
            var validatorType = Type.GetType(validatorTypeName);
            if (validatorType == null || !typeof(IValidationEngine).IsAssignableFrom(validatorType))
            {
                // 这里可以加日志记录配置错误
                continue;
            }

            _strategies.Add(propName.ToLower(), new Lazy<IValidationEngine>(() => 
                (IValidationEngine)Activator.CreateInstance(validatorType)));
        }
    }

    // Execute方法和之前一致
}

这个方案的优势是动态可调,但需要注意验证器类型名称的正确性,避免配置错误。

方案3:组合验证器简化冗余代码

你当前的MandatoryStringLengthValidator其实是MandatoryValidator和StringLengthValidator的组合,可以用组合验证器复用逻辑,减少冗余类:

public class CompositeValidator : IValidationEngine
{
    private readonly IEnumerable<IValidationEngine> _validators;

    public CompositeValidator(params IValidationEngine[] validators)
    {
        _validators = validators;
    }

    public Error Validate(string propName, dynamic propValue)
    {
        // 按顺序执行验证,返回第一个错误(也可以收集所有错误,根据需求调整)
        foreach (var validator in _validators)
        {
            var error = validator.Validate(propName, propValue);
            if (error != default)
                return error;
        }
        return default(Error);
    }
}

然后扩展特性支持多个验证器类型:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class ValidatorsAttribute : Attribute
{
    public Type[] ValidatorTypes { get; }

    public ValidatorsAttribute(params Type[] validatorTypes)
    {
        ValidatorTypes = validatorTypes;
    }
}

属性上就可以直接标记组合验证:

[Validators(typeof(MandatoryValidator), typeof(StringLengthValidator))]
public string Position { get; set; }

最后在ValidationRouter中创建组合验证器实例即可,彻底告别MandatoryStringLengthValidator这类冗余类。

总结

你的初始方案是合理的,但手动映射扩展性不足。优先推荐基于特性的配置驱动方案,既保留了策略模式的解耦优势,又极大降低了维护成本;如果需要动态调整验证策略,再考虑配置文件驱动;组合验证器则可以进一步简化代码结构,符合DRY原则。

内容的提问来源于stack exchange,提问作者Karthik Chintala

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:00:30