You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于面向对象原则的用户输入验证设计优化问询

这个问题问得特别好——很多人刚接触OO原则时,都会在“无状态工具类”和“纯粹OO设计”之间纠结,我来分享几个既能保留你想要的测试便利性,又更贴合OO思想的思路:


方案1:让Validator成为带状态的领域对象

把验证规则和错误信息封装为Validator的实例状态,让每个Validator实例对应一种特定的验证逻辑,而不是一个通用的无状态工具。

public class InputValidator {
    private final Predicate<String> validationRule;
    private final String errorMessage;

    // 通过构造函数注入规则和错误提示,赋予对象状态
    public InputValidator(Predicate<String> validationRule, String errorMessage) {
        this.validationRule = validationRule;
        this.errorMessage = errorMessage;
    }

    public void validate(String input) throws ValidationException {
        if (!validationRule.test(input)) {
            throw new ValidationException(errorMessage);
        }
    }
}

为什么符合OO?

这个类不再是“工具函数的集合”,而是封装了特定验证逻辑的对象——它的行为(validate)和自身的状态(验证规则、错误信息)绑定在一起,完全符合OO“对象封装数据与行为”的核心思想。

保留测试优势:

  • 依赖注入时,你可以根据需求注入不同的InputValidator实例(比如非空验证、长度验证);
  • 测试时,要么直接创建测试用的实例,要么mock这个类,和之前的使用体验完全一致;
  • 每个InputValidator实例可以独立测试,验证其规则是否生效。

方案2:将验证逻辑融入领域实体

如果你的验证是针对某个特定领域对象的(比如用户输入、订单信息),可以把验证逻辑直接嵌入到领域实体的构造过程中,确保对象从创建起就处于有效状态。

public class UsernameInput {
    private final String value;

    // 在构造阶段完成验证,无效输入直接抛出异常
    public UsernameInput(String value) throws ValidationException {
        if (value == null || value.isBlank()) {
            throw new ValidationException("Username cannot be empty");
        }
        if (value.length() < 3) {
            throw new ValidationException("Username must be at least 3 characters long");
        }
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}

为什么符合OO?

这是OO中“对象不变性”的典型实践——一个UsernameInput对象永远是有效的,外部不需要额外的工具来验证它。数据(用户名)和验证它的行为完全绑定在同一个对象里,从根源上避免了无效状态的出现。

保留测试优势:

  • 处理类不再依赖原始字符串,而是依赖UsernameInput对象,测试时只需验证UsernameInput的构造逻辑即可;
  • 可以通过DI注入UsernameInput的工厂类(如果需要复杂的创建逻辑),同样支持mock和独立测试。

方案3:用策略模式封装验证逻辑

定义一个验证策略接口,让不同的验证规则成为接口的实现类,每个实现类都是一个有明确职责的独立对象。

// 定义验证策略接口
public interface ValidationStrategy {
    void validate(String input) throws ValidationException;
}

// 非空验证实现
public class NonEmptyValidation implements ValidationStrategy {
    @Override
    public void validate(String input) throws ValidationException {
        if (input == null || input.isBlank()) {
            throw new ValidationException("Input cannot be empty");
        }
    }
}

// 最小长度验证实现(带状态)
public class MinLengthValidation implements ValidationStrategy {
    private final int minLength;

    public MinLengthValidation(int minLength) {
        this.minLength = minLength;
    }

    @Override
    public void validate(String input) throws ValidationException {
        if (input.length() < minLength) {
            throw new ValidationException("Input must be at least " + minLength + " characters");
        }
    }
}

为什么符合OO?

每个验证策略都是一个单一职责的对象,它们遵循“面向接口编程”的原则,各自封装一种验证逻辑,没有静态方法,完全是OO风格的设计。

保留测试优势:

  • 处理类依赖的是ValidationStrategy接口,你可以通过DI注入任意实现类,灵活切换验证规则;
  • 测试时可以mockValidationStrategy接口,或者直接测试每个实现类的逻辑,独立性和可测试性拉满。

总结

你担心的“静态工具类不符合OO”是完全正确的——静态工具本质上是过程式的,它没有封装状态,只是把函数堆在一起。而上面的方案都让验证逻辑成为了有状态或有明确职责的对象,既贴合OO的核心思想,又完美保留了你想要的两个优点:支持依赖注入与mock、可独立测试。

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

火山引擎 最新活动