Home > Software design >  Should I keep an enum attribute when it has always the same value as a result of a new inheritance?
Should I keep an enum attribute when it has always the same value as a result of a new inheritance?

Time:11-04

I have these classes:

enum Brand {
    FORD, FERRARI, TESLA, RENAULT;
}
public class Car {
    Brand brand;
    String plate;
    ...
}
//getters and setters

Imagine that for some reason, I need to make Car a superclass for two new classes: CombustionCar and ElectricCar. One of the new requierements is that ElectricCar's brand attribute must be always TESLA value and not any of the other ones values. I've thougth some solutions:

  1. I could keep Brand attr on superclass Car, and make ElectricCar constructor to set TESLA brand. But this way could allow me to set a new Brand after creating the object
public class ElectricCar extends Car {
    public ElectricCar(...){
    super(Brand.TESLA, ...);
}

ElectricCar ec = new ElectricCar(...);
ec.setBrand(Brand.FORD);
  1. I can take Brand attr out from superclass and set it on both subclasses, but setting it in ElectricCar as a class attribute with a final so anyone would be able to set a new value
public class ElectricCar extends Car {
    public static final Brand brand = Brand.TESLA;
    ...
}
public class CombustionCar extends Car {
    private Brand brand;
    ...
}
  1. Avoid inheritance and use composition, but with this I wont be able to use, for example, a List which contain both:
public class ElectricCar {
    private Car car;
    private Brand brand = Brand.TESLA;//with no setter
    ...
}
public class CombustionCar {
    private Car car;
    private Brand brand;
    ...
}

I'm asking for the most elegant and manteinable solution, I think any of them would be nice to resolve my problem.

CodePudding user response:

Your first solution is incorrect given that you required a non editable BRAND for an electric car.

Your second solution just doesn't work at all excepted if you override both getter and setter of brand field to use your static field, which is not "elegant and mantainable"

Your third solution doesn't make use of object oriented concept.

A simple solution I would use is to let the field brand and its getter in Car superclass, but I'd only define the setter in the CombustionCar class. Alternatively, if you extend your model, you could create an intermediate abstract superclass "FreeBrandCar" which implements the setter.

Solution with the setter in CombustionCar

abstract public class Car {
    protected String brand;
    protected Car(final String b) {
        this.brand = b;
    }
    public String getBrand() {
        return this.brand;
    }
}

public class ElectricCar extends Car {
    public ElectricCar() {
        super("Tesla");
    }
}

public class CombustionCar extends Car {
    public CombustionCar(final String b) {
        super(b);
    }
    public void setBrand(final String b) {
        this.brand = b;
    }
}

Solution with an intermediate class

abstract public class Car {
    protected String brand;
    protected Car(final String b) {
        this.brand = b;
    }
    public String getBrand() {
        return this.brand;
    }
}

abstract public class FreeBrandCar extends Car {
    public FreeBrandCar (final String b) {
        super(b);
    }
    public void setBrand(final String b) {
        this.brand = b;
    }
}

public class ElectricCar extends Car {
    public ElectricCar() {
        super("Tesla");
    }
}

public class CombustionCar extends FreeBrandCar {
    public CombustionCar(final String b) {
        super(b);
    }
}

It respects your requirements :

public void test() {
    ElectricCar ec = new ElectricCar();
    ec.setBrand("..."):  // Doesn't compile
    CombustionCar cc = new CombustionCar("Ford"); // OK
    cc.setBrand("Fiat"); // OK
    Arrays.asList(ec, cc)
       .stream()
       .forEach(car -> System.out.println(car.getBrand())); // prints Tesla and Fiat
}
  • Related