0

I have a class called Plant. It has a constructor like this.

int xPosition, yPosition;
String plantType;
int Size;


public Plant(String ptype, int s) {

    xPosition = xPos;
    yPosition = yPos;
    plantType = ptype;
    Size = s;

}

I want to keep track of these virtual plants. Only one plant can be in a location at a time. I decided that maybe I could use an array.

Plant[][] fieldOfPlants;
    fieldOfPlants = new Plant[100][100];

    for (int i = 0; i < 100; i++) {
        for (int j = 0; j < 100; j++) {
            fieldOfPlants[i][j] = new Plant(i, j, "none", false);
        }
    }


    // Plant the seeds at these locations.
    fieldOfPlants[12][14].plantType = "noodle plant";
    fieldOfPlants[12][14].seedPlant();

This makes sense, but isn't it bad that the i,j coordinates and the xPos, yPos coordinates can get out of sync? It seems like bad design to have the location in two places.

How to eliminate xPos and yPos from the constructor?

futurebird
  • 73
  • 7
  • Are you saying the into is in two places because it's in the Java objects and also the database? – nicomp Feb 16 '21 at 19:22
  • 4
    Does your plant really need to know where it is planted? – Robert Harvey Feb 16 '21 at 19:23
  • The answer to the question "how to eliminate xPos and yPos from the constructor" is simply to remove them from the constructor. – Robert Harvey Feb 16 '21 at 19:26
  • 1
    Or if your plant does need to know where it is planted, can you put the plant array in a static class somewhere, and add a little utility method so any plant object can look up where it is? – Charlie Armstrong Feb 16 '21 at 19:27
  • See this related [example](https://stackoverflow.com/a/7706684/230513). – trashgod Feb 16 '21 at 19:28
  • The location could impact the growth, but in theory lets say yes, I want to be able to say "this plant is in this location" – futurebird Feb 16 '21 at 19:28
  • It isn't "good" that you have duplicate information, but it isn't terrible either. One way would be to just proceed as-is and see how much trouble it causes. If the pain level never increases, then "no, it wasn't a bad design after all." – markspace Feb 16 '21 at 19:31

1 Answers1

2

A common way to do this is by use of a static class. This is essentially a class where everything is static, so you don't make any instances of this class. In this case, you would probably want a "garden" class to keep track of all of your plants. Assuming you don't plan on having multiple gardens, this will be the garden class for your application, which is why it makes sense for it to be a static class. In order to keep track of all the plants, the garden class will need to contain your "field of plants" array, and in order to be useful, it needs to implement some utility methods to access that array. Here is how I would implement the garden class:

// Notice the final modifier.  This is standard for static classes because there is
// no point in extending them.
public final class Garden {
    // Here's your "field of plants" array.  Notice it's static, so its not bound to
    // any instance.
    private static final Plant[][] FIELD_OF_PLANTS = new Plant[100][100];

    // Notice the private constructor.  This is also standard for static classes
    // because there is no point in initializing them, since everything is static.
    private Garden() { }

    // This is a utility method to get the location of a given plant.  It just does a
    // linear search through the array and returns the location when it finds a match.
    // You can find much faster algorithms, but for demonstration purposes, linear
    // search does the job.
    public static Point getPlantLocation(Plant plant) {
        for (int i = 0; i < 100; i++) {
            for (int j = 0; j < 100; j++) {
                if (FIELD_OF_PLANTS[i][j] == plant) {
                    return new Point(i, j);
                }
            }
        }

        // If we got this far, that means plant isn't anywhere in FIELD_OF_PLANTS
        throw new NoSuchElementException();
    }

    // This is a utility method going the other way, finding which plant is at a given
    // location.  It just returns the proper index in the array.
    public static Plant getPlantAt(int x, int y) {
        return FIELD_OF_PLANTS[x][y];
    }

    // This is a mutator utility method for planting a plant in the garden.
    // It just sets the appropriate index of the array to the passed in plant object.
    public static void setPlantAt(int x, int y, Plant plant) {
        FIELD_OF_PLANTS[x][y] = plant;
    }
}

Then in your plant class, you can just get rid of the location information, and you can still add methods that use the location of the plant:

public class Plant {
    // Notice the only thing the plant knows is its type and size.
    private int size;
    private final String plantType;

    public Plant(String ptype, int s) {
        plantType = ptype;
        size = s;
    }

    // This method uses the plant's location by calling the static class' utility
    // method.
    public void printAllInformation() {
        Point location = Garden.getPlantLocation(this);
        System.out.format("I am a %s and my size is %d.  I am at row %d, column %d.%n",
                plantType, size, location.y, location.x);
    }
}

You can test all of this using a simple driver class like so:

public class Driver {
    public static void main(String args[]) {
        FIELD_OF_PLANTS[12][14] = new Plant("noodle plant", 0);
        FIELD_OF_PLANTS[12][14].printAllInformation();
    }
}

The result should be:

I am a noodle plant and my size is 0.  I am at row 14, column 12.

This way you've only stored the location of each plant once, in your garden class, but each plant can still figure out where it is in the garden.

Charlie Armstrong
  • 2,332
  • 3
  • 13
  • 25