Item 23: Prefer class hierarchies to tagged classes

23. Select a class hierarchy from tagged classes

Tagged class

A class that shows two or more types of characteristics and switches between them with tags that have those characteristics in the field is called a tagged class. The following class is an example of this, and it is possible to represent circles and rectangles.

package tryAny.effectiveJava;

class Figure {
    enum Shape {
        RECTANGLE, CIRCLE
    }

    //Hold shape type
    final Shape shape;

    //Use only when shape is RECTANGLE
    double length;
    double width;

    //Use only when shape is CIRCLE
    double radius;

    //Constructor for circle
    Figure(double radius) {
        shape = Shape.CIRCLE;
        this.radius = radius;
    }

    //Constructor for rectangle
    Figure(double length, double width) {
        shape = Shape.RECTANGLE;
        this.length = length;
        this.width = width;
    }

    double area() {
        switch (shape) {
        case RECTANGLE:
            return length * width;
        case CIRCLE:
            return Math.PI * (radius * radius);
        default:
            throw new AssertionError();
        }
    }
}

Such tagged classes have many drawbacks. Below is a list of drawbacks.

Alternatives to tagged classes: class hierarchy

If it is an object-oriented language, it can be improved by using a class hierarchy.

The procedure for modifying from a tagged class to a class hierarchy is

  1. Create an abstract class and define the method (area method in the above example) whose operation is switched by the tag value as the abstract method.
  2. Put tag value-independent methods and field values in the abstract class (there is no such thing in the above example)
  3. Create a subclass for the part that corresponds to the characteristics of the tagged class. (Circle, rectangle in the above example)
  4. Put a characteristic-specific field in each subclass. (Radius for circle, length, width for rectangle)

The above correction result is as follows.

package tryAny.effectiveJava;

abstract class Figure {
    abstract double area();
}

class Circle extends Figure {
    final double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    double area() {
        return Math.PI * (radius * radius);
    }
}

class Rectangle extends Figure {
    final double length;
    final double width;

    Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    @Override
    double area() {
        return length * width;
    }
}

By doing this, the drawbacks of the tagged class mentioned above are eliminated.

Other good points of class hierarchy

Class hierarchies can reflect the true hierarchical relationships between types, which can improve flexibility (?) And improve compile-time error checking.

When it is decided to add a square type in the above example, it can be described as follows using the characteristics of the class hierarchy.

class Square extends Rectangle {

    Square(double side) {
        super(side, side);
    }
}

Recommended Posts

Item 23: Prefer class hierarchies to tagged classes
Item 42: Prefer lambdas to anonymous classes
Item 28: Prefer lists to arrays
Item 65: Prefer interfaces to reflection
Item 43: Prefer method references to lambdas
Item 39: Prefer annotations to naming patterns
Item 85: Prefer alternatives to Java serialization
Item 58: Prefer for-each loops to traditional for loops
Item 61: Prefer primitive types to boxed primitives
Choose a class hierarchy from tagged classes
Item 81: Prefer concurrency utilities to wait and notify
Item 80: Prefer executors, tasks, and streams to threads
Item 89: For instance control, prefer enum types to readResolve
Item 47: Prefer Collection to Stream as a return type
Item 25: Limit source files to a single top-level class
Class to take count