nus-cs2113-AY2223S2 / forum

10 stars 0 forks source link

Preferred method to implement data? #34

Open Brennanzuz opened 1 year ago

Brennanzuz commented 1 year ago

Say I have a parent class named Animal. Animal has a bunch of static attributes (variables) like genus, species, diet, that are currently set to nothing. Now I create a Chicken child class that extends Animal, and I want to use these static variables and set them to a fixed and final value like gallus, gallus gallus and worms and seeds.

I've read that it's impossible to override static variables (https://stackoverflow.com/questions/19457114/java-overriding-static-variable-of-parent-class), and that such a behavior can be remedied with a database, but since we cannot implement a database for our team project, what should we do?

woowenjun99 commented 1 year ago
  1. Make these variables non-static if they only apply to the Chicken subclass.
  2. Create a subclass of Bird that extends Animal and have the static variables stored in them.
  3. Create a class of meta data that keeps track of the data.
nichyjt commented 1 year ago

Wen Jun brings up good ideas!

To add on, I think that in terms of design[^1], static variables should only be used for properties that we know for sure will not change across the subclasses, like pre-set string messages.

[^1]: Here's a good StackOverflow post discussing the usage of static.

In this case, since genus, species and diet change across subclasses, static may not be appropriate to use here.

Based on the context given and variable naming, I'm guessing that the intent behind these variables are being set as static final is to:

  1. Prevent accidentally changing the values
  2. Have an easy way to access these unchangeable (constant) values

If that's the case, perhaps it may be better to use a get approach and programatically set the 'constant' values on construction?

The idea: within Animal and its subclasses, assign genus, species, diet in the constructor and only expose a getter function. If you want to make it even more watertight, declare the getter functions as final to prevent overriding.

class Cat extends Animal {

    // Your psuedo-constants
    private String genus;
    private String diet;
    private String species;

    public Cat(){
        super();
        this.genus = "Felis";
        this.species = "Felis catus";
        this.diet = "Fish and caviar";
    }

    // Only expose getter methods to the psuedo constant variables
    // use final to prevent this method from being overriden
    public final String getSpecies() {
        return this.species;
    }   
    //...
}

This way, although the 3 variables are not true 'constants', this implementation still meets the (1.) and (2.) requirements without needing to do much hacking around static final to overcome Java language restrictions!

But if there really is a need to create static variables, then it is technically possible to change the state if you use Container-based classes like ArrayList or HashMap. You could store your metadata in a container of your choice and use the .add() .get() methods (or their equivalent). This is legal as the container object on heap is not being destroyed. I wouldn't recommend this approach though, as it may be harder to maintain in the long run.

okkhoy commented 1 year ago

From the initial question:

Animal has a bunch of static attributes (variables) like genus, species, diet, ...

I don't understand why you want them to be static? These are attributes of each animal, correct? If your intention is to provide the default structure to all Animals just make them protected members and set the values in each of the subclasses.

Both @woowenjun99 and @nichyjt give excellent suggestions to achieve what you want. The only thing which deviates from what you asked in @nichyjt 's response is that he is suggesting creating private variables in each of the subclass, I am suggesting making them protected, so that you can reuse in the sub classes.