简体   繁体   中英

Better to change parent's field or override getter?

Let's say I have a Projectile class which acts as a base class for all projectiles in my game. This contains default values for maximum speed, gravity coefficient, bounce coefficient, etc.

public abstract class Projectile {

    protected float maxSpeed = 100.0f;
    protected float gravityCoefficient = 1.0f;
    protected float bounceCoefficient = 1.0f;
    ...

}

I then have a bunch of subclasses, each of which may choose to override some of these default values.

Which is the better approach here?

1. Set field values in child constructor

public class Arrow {

    public Arrow(){
        super();
        maxSpeed = 200.0f;
    }

}

2. Make child override getter

public class Arrow {

    public float getMaxSpeed(){
        return 200.0f;
    }

}

I am inclined to say that the first approach is better, since it means the field can be accessed directly without the need for any extra function calls. However, it does mean that the value is set twice during object creation, once by the parent and once by the child.

Am I missing anything here? Is there, perhaps, another approach?

Intuitively, the maximum speed of any particular projectile is unlikely to vary over its lifetime (even in the case where different instances of the same type can have different maximum speeds), therefore I would favour a final field for it. I would also favour making it final - I very rarely use non-private fields, other than for genuine constants.

As you have some state (the field) for Projectile , I would avoid allowing the confusion of having the maximum speed has revealed by getMaxSpeed differing from the field.

I would probably design it like this:

public abstract class Projectile {
    private final float maxSpeed;

    protected Projectile(float maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

    // Only if you really need this...
    protected Projectile() {
        this(200f);
    }

    public final getMaxSpeed() {
        return maxSpeed;
    }
}

public class Arrow extends Projectile {
    public Arrow() {
        super(100f);
    }
}

The gravity coefficient and bounce coefficient may be treated in a similar way - or if all of these really act as "the same values for every instance of a particular type" you could introduce a new class to represent these constants, which separates the varying state of instances of a type from the constant restrictions/coefficients - and each instance could just have a final reference to an instance of that new class. Unfortunately Java (and at least some similar languages) don't really model this kind of hierarchy well. It's always an annoyance :(

you should have a setter and use it, that is what setters are for. It will allow you to keep the field private. The other benefit is that using Java Bean convention will allow you to use libraries such as Apache Commons BeanUtils to populate and manipulate your Objects. You will also be able to persist your data in DB or file.

public abstract class Projectile {

    private float maxSpeed = 100.0f;  // default 

    protected void setMaxSpeed(float newSpeed) {
        maxSpeed = newSpeed;
    }
}

public class Arrow extends Projectile {

    public Arrow() {
        super();
        setMaxSpeed(200.0f);  // arrow specific values
    }
}

First approach. Declare a method named modifyDefaults() in your abstract base class. Implement it in each class and call in the constructor so that whenever someone sees abstract class, it can be concluded that you will be modifying defaults in children.
Or just hand over responsibility of Projectile creation to a projectileFactory if there are only a few deciding parameters.

Your inclination towards the first answer should be. It does clearly state the following:

  • The child class has it's responsibility of creating it's own instance variables (properties)

  • Overriding of getter, though sounds good at certain views, doesn't usually give a good maintainability. Constructor clearly states the extra set of property defaults whatsoever very cleanly.

I'm not sure about your design, but if you have your super class which doesn't have state of it's own, try making it abstract and the design changes completely than we discussed in that case (option 2 might be considered that time).

For java, compiler optimization and JIT optimization are of great importance for the improvement of performance.
The second piece of code will be much more easier to be optimized with no worry of extra operations.

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