在构造函数中抛出异常是否合理?学生项目类参数校验咨询
Hey there! Great question—validating input during object creation is absolutely a good idea (you want to avoid having invalid Triangle objects floating around your code!), but your current implementation has a few issues we can fix, plus there are alternative approaches worth considering.
First: Fix Your Current Constructor Validation
Let’s start with the problems in your existing code:
- You’re swallowing exceptions: Your
isValid()method catchesExceptionand only prints a stack trace, but doesn’t propagate the error. That means even if invalid values are passed, the constructor will still create a brokenTriangleobject. That defeats the whole purpose of validation! - Generic exceptions are unhelpful: Throwing a raw
Exceptionmakes it hard for callers to handle specific validation errors. Use more specific exceptions likeIllegalArgumentException(or a custom exception likeInvalidTriangleExceptionfor clarity). - Redundant parameters: You’re passing both
heightand the three sides (base,sideB,sideC). A triangle’s height can be calculated from its sides, so including both risks inconsistencies (e.g., a height that doesn’t match the given sides). Stick to one set of parameters unless you have a specific reason to include both.
Here’s a revised version of your code that fixes these issues:
public class Triangle { private final double sideA; private final double sideB; private final double sideC; // Constructor with only three sides (height can be calculated later) public Triangle(double sideA, double sideB, double sideC) { validateSides(sideA, sideB, sideC); this.sideA = sideA; this.sideB = sideB; this.sideC = sideC; } private void validateSides(double a, double b, double c) { if (a <= 0 || b <= 0 || c <= 0) { throw new IllegalArgumentException("All sides must be greater than 0!"); } if (!satisfiesTriangleInequality(a, b, c)) { throw new IllegalArgumentException("Sides do not form a valid triangle!"); } } private boolean satisfiesTriangleInequality(double a, double b, double c) { return (a + b > c) && (a + c > b) && (b + c > a); } // Optional: Calculate height if needed public double calculateHeight(double baseSide) { // Implement Heron's formula here to compute height from sides double semiPerimeter = (sideA + sideB + sideC) / 2; double area = Math.sqrt(semiPerimeter * (semiPerimeter - sideA) * (semiPerimeter - sideB) * (semiPerimeter - sideC)); return (2 * area) / baseSide; } }
Alternative Validation Approaches
While constructor validation works, here are two popular patterns that might make your code more flexible and readable:
1. Static Factory Methods
Instead of using a public constructor, create a static method that handles validation and returns a Triangle instance. This lets you give meaningful names to different creation scenarios and keeps validation logic tied to object creation:
public class Triangle { private final double sideA; private final double sideB; private final double sideC; // Make constructor private to force use of factory methods private Triangle(double sideA, double sideB, double sideC) { this.sideA = sideA; this.sideB = sideB; this.sideC = sideC; } public static Triangle fromSides(double a, double b, double c) { validateSides(a, b, c); return new Triangle(a, b, c); } // Add other factory methods if needed, e.g., fromBaseAndHeight public static Triangle fromBaseAndHeight(double base, double height) { if (base <= 0 || height <= 0) { throw new IllegalArgumentException("Base and height must be greater than 0!"); } // Calculate the other two sides for a right triangle example double hypotenuse = Math.sqrt(Math.pow(base/2, 2) + Math.pow(height, 2)); return new Triangle(base, hypotenuse, hypotenuse); } // Same validateSides and satisfiesTriangleInequality methods as before }
2. Builder Pattern
If you ever end up with more parameters (or optional parameters), the Builder pattern lets you set values step-by-step and validate them all at once when building the object:
public class Triangle { private final double sideA; private final double sideB; private final double sideC; private Triangle(Builder builder) { this.sideA = builder.sideA; this.sideB = builder.sideB; this.sideC = builder.sideC; } public static class Builder { private double sideA; private double sideB; private double sideC; public Builder sideA(double value) { this.sideA = value; return this; } public Builder sideB(double value) { this.sideB = value; return this; } public Builder sideC(double value) { this.sideC = value; return this; } public Triangle build() { validateSides(sideA, sideB, sideC); return new Triangle(this); } private void validateSides(double a, double b, double c) { if (a <= 0 || b <= 0 || c <= 0) { throw new IllegalArgumentException("All sides must be greater than 0!"); } if ((a + b <= c) || (a + c <= b) || (b + c <= a)) { throw new IllegalArgumentException("Sides do not form a valid triangle!"); } } } } // Usage: Triangle triangle = new Triangle.Builder() .sideA(3) .sideB(4) .sideC(5) .build();
Final Thoughts
- Constructor validation is totally acceptable if done correctly (don’t swallow exceptions, use specific exceptions).
- Static factory methods are great for making object creation explicit and grouping validation logic.
- The Builder pattern shines when you have multiple parameters or want to make object creation more flexible.
Pick the approach that fits your project’s needs—for a student project, even the revised constructor approach will work well once you fix the exception handling!
内容的提问来源于stack exchange,提问作者user8799994

