基于typeName值实例化类特定属性的最优设计方案咨询
Great question! Your current if-else approach works for now, but it’s not scalable—adding a new TYPE3 later would force you to modify existing code (breaking the Open/Closed Principle) and leave unused attributes hanging around in CentralClass, which is a recipe for bugs. Let’s walk through two clean, maintainable solutions that fix this, depending on your needs.
Option 1: Inheritance + Simple Factory Pattern (Most Recommended)
This approach splits your mixed attributes into dedicated subclasses, enforcing single responsibility and making it easy to add new types later.
Step 1: Refactor Class Hierarchy
Create an abstract parent class for shared logic, then subclasses for each type with their own properties:
// Abstract base class for all types public abstract class CentralClass { protected final String typeName; protected CentralClass(String typeName) { this.typeName = typeName; } public String getTypeName() { return typeName; } } // Subclass for TYPE1 with its exclusive properties public class Type1Class extends CentralClass { private final String a; private final String b; private final String c; public Type1Class(String a, String b, String c) { super("TYPE1"); this.a = a; this.b = b; this.c = c; } // Getters for TYPE1 properties public String getA() { return a; } public String getB() { return b; } public String getC() { return c; } } // Subclass for TYPE2 with its exclusive properties public class Type2Class extends CentralClass { private final String x; private final String y; private final String z; public Type2Class(String x, String y, String z) { super("TYPE2"); this.x = x; this.y = y; this.z = z; } // Getters for TYPE2 properties public String getX() { return x; } public String getY() { return y; } public String getZ() { return z; } }
Step 2: Implement a Factory Class
The factory handles instantiation based on typeName, so you don’t need scattered if-else checks:
public class CentralClassFactory { public static CentralClass createCentralClass(String typeName, Object... args) { return switch (typeName) { case "TYPE1" -> new Type1Class((String) args[0], (String) args[1], (String) args[2]); case "TYPE2" -> new Type2Class((String) args[0], (String) args[1], (String) args[2]); default -> throw new IllegalArgumentException("Invalid typeName: " + typeName); }; } }
Step 3: Usage Example
public class Main { public static void main(String[] args) { CentralClass type1Instance = CentralClassFactory.createCentralClass("TYPE1", "aValue", "bValue", "cValue"); CentralClass type2Instance = CentralClassFactory.createCentralClass("TYPE2", "xValue", "yValue", "zValue"); // Access type-specific properties with type checking (safe in Java 16+) if (type1Instance instanceof Type1Class type1) { System.out.println("TYPE1 a: " + type1.getA()); } } }
Why This Works:
- Single Responsibility: Each subclass only manages properties relevant to its type.
- Open/Closed Principle: Add new types by creating a new subclass and updating the factory—no changes to existing code.
- Type Safety: Constructors only accept the required parameters, eliminating accidental invalid property assignments.
Option 2: Builder Pattern with Type Constraints (If You Want to Keep a Single Class)
If you prefer to retain a single CentralClass but still enforce type-specific property rules, use a builder with validation:
Refactored CentralClass with Builder
public class CentralClass { private final String typeName; private final String x; private final String y; private final String z; private final String a; private final String b; private final String c; // Private constructor—only builder can create instances private CentralClass(Builder builder) { this.typeName = builder.typeName; this.x = builder.x; this.y = builder.y; this.z = builder.z; this.a = builder.a; this.b = builder.b; this.c = builder.c; } public static Builder builder(String typeName) { return new Builder(typeName); } public static class Builder { private final String typeName; private String x; private String y; private String z; private String a; private String b; private String c; private Builder(String typeName) { this.typeName = typeName; } // TYPE1-only property setters with validation public Builder a(String a) { enforceTypeRestriction("TYPE1"); this.a = a; return this; } public Builder b(String b) { enforceTypeRestriction("TYPE1"); this.b = b; return this; } public Builder c(String c) { enforceTypeRestriction("TYPE1"); this.c = c; return this; } // TYPE2-only property setters with validation public Builder x(String x) { enforceTypeRestriction("TYPE2"); this.x = x; return this; } public Builder y(String y) { enforceTypeRestriction("TYPE2"); this.y = y; return this; } public Builder z(String z) { enforceTypeRestriction("TYPE2"); this.z = z; return this; } // Validate required properties before building public CentralClass build() { if ("TYPE1".equals(typeName)) { if (a == null || b == null || c == null) { throw new IllegalStateException("TYPE1 requires a, b, c to be set"); } } else if ("TYPE2".equals(typeName)) { if (x == null || y == null || z == null) { throw new IllegalStateException("TYPE2 requires x, y, z to be set"); } } else { throw new IllegalArgumentException("Invalid typeName: " + typeName); } return new CentralClass(this); } private void enforceTypeRestriction(String allowedType) { if (!allowedType.equals(typeName)) { throw new IllegalStateException("Cannot set this property for type " + typeName); } } } // Getters for all properties public String getTypeName() { return typeName; } public String getX() { return x; } public String getY() { return y; } public String getZ() { return z; } public String getA() { return a; } public String getB() { return b; } public String getC() { return c; } }
Usage Example
public class Main { public static void main(String[] args) { // Valid TYPE1 instance CentralClass type1 = CentralClass.builder("TYPE1") .a("aVal") .b("bVal") .c("cVal") .build(); // Valid TYPE2 instance CentralClass type2 = CentralClass.builder("TYPE2") .x("xVal") .y("yVal") .z("zVal") .build(); // This will throw an exception (trying to set x for TYPE1) // CentralClass invalid = CentralClass.builder("TYPE1").x("xVal").build(); } }
Why This Works:
- Single Class: Keeps all properties in one place if that’s your preference.
- Runtime Validation: Prevents setting invalid properties for a given type.
- Readable Syntax: Chained builder calls make instance creation easy to follow.
Which to Choose?
- Go with Inheritance + Factory if different types have distinct business logic (not just different properties)—it’s more flexible for future changes.
- Use the Builder Pattern if you only need to restrict properties but keep shared logic in a single class.
内容的提问来源于stack exchange,提问作者Akshit Gupta

