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

‘验证型’设计模式咨询:受限值包装模式名称及Java工具库查询

Constrained Value Objects: Pattern Name & Tooling Guide

Hey there! Let's tackle your question about wrapping values into objects that enforce strict constraints on their internal values—plus the tools that can save you from writing error-prone custom implementations.

What's This Pattern Called?

This approach is a specialized variant of the Value Object pattern, often referred to as a Constrained Value Object or Strongly-Typed Value Object. The core idea is to encapsulate primitive types (like integers or strings) alongside validation rules, so invalid values can't even exist as valid instances of the object. This turns runtime validation failures into compile-time clarity (as much as possible in a language like Java, which lacks dependent types).

Example Implementation (Your Range/Percentage Type)

As you showed, in Java we can work around the lack of dependent types by using subtyping to lock in constraints. Here's your example, formatted cleanly:

class Range{ 
    final private int _tLower; 
    final private int _tUpper; 
    final private int _value; 
    protected Range(int lower, int upper, int value){ 
        _tLower = lower; 
        _tUpper = upper; 
        _value = value; 
        verify(); 
    } 
    private void verify(){ 
        if(_value <= _tUpper && _value >= _tLower){ 
            return; 
        } 
        throw new RuntimeException("Value " + _value + " out of bounds: [" + _tLower + ", " + _tUpper + "]"); 
    } 
    int getValue(){ return _value;} 
} 
class PercentageInt extends Range{ 
    public PercentageInt(int value){ 
        super(0, 100, value); 
    } 
}

By creating PercentageInt as a subclass of Range, you bake the 0-100 constraint directly into the type. Other developers will immediately know to use this instead of raw int or Integer, since the type itself communicates the allowed values.

Why Use This Pattern?

The biggest win is separating validation from business logic:

  • Validation only runs once, when the object is created—no need to re-check values every time you use them
  • The type acts as self-documentation: no more guessing what values are allowed for a parameter or return type
  • You eliminate entire classes of bugs caused by invalid values slipping through into your system

Common Use Cases Beyond Ranges

This pattern isn't just for numeric ranges—here are a few more practical applications:

  • Inequality-bound pairs: Objects that hold two values where one must always be less than or equal to the other (like start/end timestamps)
  • Regex-constrained strings: Types for emails, phone numbers, or custom codes that enforce a specific format via regex at creation time
  • Domain-specific codes: Values that must match a fixed set of business rules (like ISO country codes, or internal product SKU formats)

Critical Rule: Keep These Objects Immutable

Constrained value objects must be immutable. If you ever add a method to modify the internal value, you must re-run the validation logic to ensure the new value still meets the constraints. Without immutability, you risk invalid values sneaking in after the object is created, which defeats the whole purpose of the pattern.

Formal Alternatives (And Why They're Not Practical)

You mentioned formal approaches like JML or dependent type workarounds, but they're rarely used in real-world Java projects:

  • JML (Java Modeling Language): Requires adding verbose pre/post condition annotations and external tooling to enforce them. The learning curve is steep, and it doesn't integrate smoothly with standard Java workflows.
  • Dependent type emulations: Java doesn't support true dependent types, so any workaround would be overly complex, require custom compiler plugins, or introduce significant runtime overhead.

Tool Libraries to Avoid Custom Implementations

You don't have to write all this validation logic from scratch! Here are some solid libraries for Java:

  • Vavr: Its Validation API lets you combine multiple validation rules and handle errors gracefully, making it easy to build constrained value objects without boilerplate.
  • Immutables: A code-generation library that creates immutable value objects from annotated interfaces. You can embed validation logic directly in the annotations, and it handles all the repetitive code (constructors, equals, hashCode) for you.
  • Apache Commons Validator: While not a dedicated value object library, it provides pre-built validation rules (for emails, dates, numeric ranges) that you can plug into your custom value object constructors.

If you're open to other JVM languages, Scala's Refined or Kotlin's kotlin-refined offer compile-time validation for constrained types, which is even more powerful than Java's runtime approach.


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:28:03