Search code examples
javaclassfactoryfactory-methodconstruction

Multiple factory methods versus single method


Is it better to use a single factory method and a general constructor for all instances, then populate the instances? OR should multiple factory methods and constructors be used instead? What are the advantages to each approach?

For example (Option #1):

VehicleFactory {
  Vehicle createVehicle(int serialNo);
}

// assuming that Car and Plane are of type Vehicle:

Car car = (Car)VehicleFactory.createVehicle(serialNo1);
car.setCarSpecificField1(...);
car.setCarSpecificField2(...);

Plane plane = (Plane)VehicleFactory.createVehicle(serialNo2);
plane.setPlaneSpecificField1(...);
plane.setPlaneSpecificField2(...);

Here Plane and Car will have a simple general constructor, but would require multiple setters and getters. The caller would have to populate the instances.

Or (Option #2):

VehicleFactory {
  Car createCar(int serialNo, CarSpecificField1 field1, CarSpecificField2 field2, ...)
  Plane createPlane(int serialNo, PlaneSpecificField1 field1, PlaneSpecificField2, ...)
}

Car car = VehicleFactory.createCar(serialNo1, carSpecificField1, ...);
Plane plane = VehicleFactory.createPlane(serialNo2, planeSpecificField1, ...);

Here, we don't need getter and setters but would need different constructors for each instance.


Solution

  • You could do something like this, but keep in mind that inheriting builder methods from a superclass means you must set all car-specific fields first (or do lots of casting).

    abstract class Vehicle {
        Object vehicleSpecificField1, vehicleSpecificField2;
    }
    
    class Car extends Vehicle {
        Object carSpecificField;
    
        Car(final Object vehicleSpecificField1, final Object vehicleSpecificField2, final Object carSpecificField) {
            this.vehicleSpecificField1 = vehicleSpecificField1;
            this.vehicleSpecificField2 = vehicleSpecificField2;
            this.carSpecificField = carSpecificField;
        }
    }
    
    abstract class VehicleBuilder<E extends Vehicle> {
        Object vehicleSpecificField1, vehicleSpecificField2;
    
        public VehicleBuilder<E> vehicleSpecificField1(final Object vehicleSpecificField1) {
            this.vehicleSpecificField1 = vehicleSpecificField1;
            return this;
        }
    
        public VehicleBuilder<E> vehicleSpecificField2(final Object vehicleSpecificField2) {
            this.vehicleSpecificField2 = vehicleSpecificField2;
            return this;
        }
    
        abstract E create();
    
    }
    
    class CarBuilder extends VehicleBuilder<Car> {
        Object carSpecificField;
    
        public CarBuilder carSpecificField(final Object carSpecificField) {
            this.carSpecificField = carSpecificField;
            return this;
        }
    
        @Override
        Car create() {
            return new Car(vehicleSpecificField1, vehicleSpecificField2, carSpecificField);
        }
    }
    
    public static void main(String[] args) {
        Car car = new CarBuilder().carSpecificField("car").vehicleSpecificField1("foo").vehicleSpecificField2("bar").create();
    }