简体   繁体   中英

Avoiding code duplication with Java abstract methods

I have a class (let's say Vehicle) with a rather large number of subclasses (Car, Bike, ..). Every subclass stores specific information on the type of the subclass (# of tires, ..).

I want to ensure that all this information is already enforced (at compile-time). So I don't want to specify this data in the constructor of the subclass (because I might forget to set some of them). I could put this info in the constructor of Vehicle, but this would clutter code quite a lot, since I have many of those parameters.

public class Vehicle {
  int numberOfTires;

  public Vehicle(int numberOfTires, ...) {
    ...
  }

}

public class Bike {
  public Bike() {
    super(2,...);
    ...
  }

}

I end up with completely unreadable constructors. It would also store this info per instance, even though it is specific to the subclass.

An alternative way is to introduce abstract static getters/setters and store the info in the subclasses.

public class Vehicle {

  ...
  abstract public int getNumberOfTires();

}

public class Bike {

    static int numberofTires = 2;

    ...

    public int getNumberOfTires() {
      return numberOfTires;
  }

}

This seems way cleaner and also stores the info per subclass and not per instance but there will be a lot of code duplication in the subclasses. Right now, all the subclasses contain ~20 setters/getters but virtually no real functionality. Is there a clean way of avoiding this? Perhaps using the Factory method or alike?

In your shoes, I'd do it this way

public abstract class Vehicle {
    public abstract int getNumberOfTires();
}

and

public class Bike extends Vehicle {

    @Override
    public int getNumberOfTires() {
        return 2;
    }

}

and

public class Car extends Vehicle {

    @Override
    public int getNumberOfTires() {
        return 4;
    }

}

Seems to me that what you are talking about here are constants, eg values that are not supposed to change at runtime. In Java, constants are variables with the following qualifiers: static final , along with private / public . Then, you won't need setters, since a Bike will never anything other than 2 wheels for instance.

I don't really see the problem with having lots of getters and setters, your Bike -class has a set of properties describing it, and they are all needed. So something like:

public abstract class Vehicle {
    public abstract int numberOfTires();
    public abstract boolean hasEngine();
}

public class Bike extends Vehicle {

    private static final int NUMBER_OF_TIRES = 2;
    private static final boolean HAS_ENGINE = false;

    public int numberOfTires() {
        return NUMBER_OF_TIRES;
    }

    public boolean hasEngine() {
        return HAS_ENGINE;
    }
}

These variables are properties of the entity that you are representing, and as per object-oriented principles, they belong as members of the class.

All domain classes will have a number of variables, and in most cases they will require at least a getter, there is no way around this. Still, it is good to keep your domain classes as small as possible, not necessarily with regards to lines of code, concepts it represents. If a domain class has grown large, decompose it and group variables that belong together is separate classes. Then each of the smaller classes will have a constructor with a limited amount of variables, and instance creation

If all that matters in limiting the amount of code in your subclasses you could do something like the code below. I'm not sure if I would recommend it though, and I don't think I'd do this in practice.

public abstract class VehicleInfo {
    public abstract int numberOfWheels();
}

public class BikeInfo extends VehicleInfo {
    @Override
    public int numberOfWheels() {
        return 2;
    }
}

public class CarInfo extends VehicleInfo {
    @Override
    public int numberOfWheels() {
        return 4;
    }
}

public class Vehicle {
    final VehicleInfo info;
    Vehicle(final VehicleInfo info) {
        this.info = info;
    }

    public int numberOfWheels() {
        return info.numberOfWheels();
    }
}

public class Bike extends Vehicle {

    public Bike() {
        super(new BikeInfo());
    }
}

public class Car extends Vehicle {

    public Car() {
        super(new CarInfo());
    }
}

This way all getters are located in the superclass (as well as in the info-classes), and the subclasses can stay clean.

You should put in your Vehicule class everything that is common. If the number of tires is a common attribute of all your Vehicule , than it belongs there.

Using the factory pattern you could avoid having to write each time you want a instance of a Bike all that is needed in the Vehicule class as the factory would do that. Meaning the code that sets the parameters of a Bike is written in one place, the BikeFactory class.

That would make the line creating a Bike looks like

Bike yourBike = BikeFactory.getInstance().create();

instead of

Bike yourBike = new Bike(numberOfTires, ... );

The factory would have either a line that looks like the above one or a bunch of calls to setters. I would recommend you use setters and just a new Bike() constructor without parameter.

The naming of the factory, methods and the factory being a singleton are only as example and can be implemented as you see fit in your application.

As mentionned, you could also use another class for the parameters but that only move the hassle of settings your parameters somewhere else.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM