为何使用建造者模式而非参数对象?
Great question—this is one of those design pattern nuances that trips up a lot of developers when they first encounter it! Reducing constructor parameter bloat is a side effect of the Builder Pattern, but its real value lies in solving a bunch of other pain points that a simple data structure (like a plain DTO or map) can’t address. Let’s break this down:
Readability & Intent Clarity
A long constructor call or a data structure with 10 fields forces you to mentally map values to parameters. With a builder, you get self-documenting code:// Hard to parse at a glance—what does the boolean even mean? User user = new User(123, "Alice", true, "alice@example.com", 28); // Builder makes intent explicit User user = new UserBuilder() .withId(123) .withName("Alice") .withAdminPrivileges(true) .withEmail("alice@example.com") .withAge(28) .build();No more guessing which parameter corresponds to which field—each method name tells you exactly what you’re setting.
Enforcing Immutability
A plain data structure (like a mutable POJO with setters) lets anyone modify its fields after creation, which can lead to unexpected bugs in multi-threaded code or when passing objects around. Builders let you create immutable objects: once you callbuild(), the resulting object has no setters, and all fields are final. The builder itself is temporary and can be discarded after creating the immutable instance.Parameter Validation & Consistency
A simple data structure doesn’t enforce rules about required fields or valid combinations. Builders let you validate parameters before creating the object, ensuring you never end up with an invalid state:public User build() { if (name == null || name.isEmpty()) { throw new IllegalArgumentException("Name cannot be empty"); } if (email == null || !email.contains("@")) { throw new IllegalArgumentException("Invalid email format"); } return new User(id, name, email, isAdmin, age); }You can also validate dependencies between fields (e.g., "if isAdmin is true, then adminRole must be set")—something you can’t easily do with a plain constructor or data structure.
Step-by-Step Construction
Sometimes you need to build an object incrementally, not all at once. For example, if you’re collecting user input across multiple UI screens, a builder lets you set fields as you get them, then finalize the object when all data is ready. A plain data structure could work here, but builders enforce that you can’t use the object until it’s fully built.Supporting Multiple Object Variants
Builders make it easy to create different versions of an object without cluttering your code with multiple constructors or overloaded methods. For example, you could add abuildGuestUser()method to your builder that pre-sets default values for guest accounts, orbuildAdminUser()that enables admin privileges automatically. This keeps your object’s constructor clean and focused on core initialization.Avoiding Parameter Order Mistakes
With a long constructor, it’s all too easy to swap two parameters of the same type (e.g., swappingageandidif both are integers) and create a bug that’s hard to track down. Builders eliminate this risk because every field is set explicitly by name, not position.
So while a simple data structure can reduce constructor parameters, it doesn’t solve all these additional problems. The Builder Pattern is about creating robust, readable, and consistent objects—not just making your constructor calls shorter.
内容的提问来源于stack exchange,提问作者kidmosey

