-1

Couldn't find a relevant explanation online I have homework to do I should create a class called Shapes and 2 more classes one for rectangle and the second is for a circle. I wanted to know what is the best way to arrange constructors, data members and methods because for example for a rectangle I should have height and width and the circle has a radius.

public class Shapes {

    //Should I use only common Data members, constructors and functions in the base class?
    private int x;
    private int y;
    private int width;
    private int height;
    private String color;
    private double radius;

also how do I create the relevant constructors using super()? I think I got it all mixed up:

//Constructors:
    public Shapes() {

    }

    //Common constructor
    public Shapes(int x, int y, String color) {
        setX(x);
        setY(y);
        setColor(color);

    }
    //Circle constructor:
    public Shapes(int x, int y, String color, double radius) {
        this(x, y, color);
        setRadius(radius);
    }

    //Rectangle constructor:
    public Shapes(int x, int y, int width, int height, String color) {
        this(x, y, color);
        setWidth(width);
        setHeight(height);
    }

in the rectangle class it looks like this:

public Rectangle() {
    super();
}

public Rectangle(int x, int y, int width, int height, String color) {
    super(x, y, width, height, color);
}

and in the circle class I did it like that:

public Circle() {
    super();
}

public Circle(int x, int y, String color, double radius) {
    super(x, y, color, radius);
}

I need a print method to print all info from each class that is relevant to the class is there any way using this print method in the base (shapes) class to avoid multiple print methods?

There are different parameters to show but we've been told to avoid multiplication of the code.

  • 1
    You mustn't put everything into `Shapes`. The shape specific things should be in their respective classes, i.e. `radius` should exist in `Circle`, not in `Shapes` or `Rectangle`. Your constructor/super confusion is the result of you misunderstanding how to build superclasses and subclasses. – Kayaman Aug 29 '16 at 18:18
  • 1
    `Shape` should be an abstract class that only handles `x`, `y`, and `color`. The concrete classes can have `width`, `height`, and `radius` in the classes where they belong. – 4castle Aug 29 '16 at 18:20

4 Answers4

1

A base class should never have any properties that are not common to all of its subclasses. In fact, it shouldn't even know about its subclasses. For more information, read about the Liskov Substitution Principle and the Open/Closed Principle.

Therefore the base class should probably only have the parameters x, y and color. So the Shapes constructor could be like this:

 public Shapes(int x, int y, String color) {
    setX(x);
    setY(y);
    setColor(color);
}

and the Circle constructor like this:

public Circle(int x, int y, String color, double radius) {
    super(x, y, color);
    this.radius = radius;
}

The way you overloaded the base class constructors for fitting the different derived classes is really a very bad design. Not only does it contradict the aforementioned principles. It will also be very hard to understand for someone who reads the code.

Frank Puffer
  • 8,135
  • 2
  • 20
  • 45
0

Only put members in a super class if they will make sense for all sub-classes. You should also be using an abstract class to prevent creation of a plain Shape object, and the protected keyword in order to hide members from the outside world, but allow sub-classes to view them.

public abstract class Shape {
    protected int x;
    protected int y;
    protected String color;

    protected Shape() { this(0, 0, ""); }
    protected Shape(int x, int y, String color) {
        this.x = x;
        this.y = y;
        this.color = color;
    }
    // setter and getters for x, y, and color
}

public class Rectangle extends Shape {
    private int width;
    private int height;

    public Rectangle() { this(0, 0, "", 0, 0); }
    public Rectangle(int x, int y, String color, int width, int height) {
        super(x, y, color);
        this.width = width;
        this.height = height;
    }
    // setters and getters for width and height
}

public class Circle extends Shape {
    private double radius;

    public Circle() { this(0, 0, "", 0.0); }
    public Circle(int x, int y, String color, double radius) {
        super(x, y, color);
        this.radius = radius;
    }
    // setter and getter for radius
}
4castle
  • 32,613
  • 11
  • 69
  • 106
  • We still haven't covered the protected and abstract subjects but thank you for the info I'll look it up and try using it after understanding exactly how it works. appreciate it. – Adam Reisher Aug 29 '16 at 18:39
0

Let's start with the attributes. You should think if an attribute applies for subclasses of a class, or only for some of them. For example, does it make sense to put a "radius" attribute in the Shapes class? Will all subclasses of Shape need that attribute? Think of Rectangle: does a rectangle (instance) have a radius? If you declare the radius attribute in Shapes, what would be its value for rectangle instances? Avoid having spare attributes in classes, for perfomance but mainly for readability. The same applies to width and height.

Now, what about color? Will each and every shape have a color? So, it is a shared attribute?.

The x and y attributes are some kind of special here: the same attribute can mean two completely different things: for a circle should it be the center, but for rectangles could be the top-left (or whatever) point. Should them be the same attribute, or should be two different pair of attributes? I would go for the second, but it's my opinion.

Finally, regarding the constructors: would you let someone to create a shape instance without being either a circle or a rectangle? Do you need a public constructor in the Shapes class? Does have sense a shape being just a shape and not a circle or a rectangle (or any other subclass of Shape)?

By the way: I think it would be better to rename your Shapes class to just Shape.. a class represents one concept; then you can create instances of that concept, which are shapes...

=== EDIT ===

I forgot the last part of your question about print methods: one you decide where to put each attribute, you will find and answer for yourself.. can a method of a class access attributes in some of their subclasses?

Oh, something else: the toString method is allways present because it is defined in the Object class.. think about the relation of the toString method and the print method you need.

0

Make the shape class an abstract class with one abstract method which all the other classes will have to define

public abstract class Shape {
    protected int x;
    protected int y;
    protected String color;

    public Shape(String color) {
        this.color = color;
    }

    public Shape() {
        this("0xfffffff");
    }

    public Shape(int x, int y, String color) {
        this(color);
        this.x = x;
        this.y = y;
    }

    public Shape(int x, int y) {
        this();
        this.x = x;
        this.y = y;
    }

    public int getX() { return x; }
    public int getY() { return y; }
    public String getColor() { return color; }

    public void setX(int x) { this.x = x; }
    public void setY(int y) { this.y = y; }
    public void setColor(String color) { this.color = color; }

    @Override
    public abstract String toString();
}

public class Rectangle extends Shape {
    private int width;
    private int height;

    public Rectangle(int width, int height){
        super();
        this.width = width;
        this.height = height;
    }

    public Rectangle(int width, int height, String color){
        super(color);
        this.width = width;
        this.height = height;
    }

    public int getWidth() { return width; }
    public int getHeight() { return height; }

    // Also include setters if you don't need immutability

    @Override
    public String toString() {
        return String.format("Rectangle\nWidth: %d\nHeight: %d\nColor: %s", width, height, color);
    }
}

This is just my opinion. There are many other ways of structuring these classes, but it all depends on how you want to use them. The way I have provided makes it easy to extend this into 3D, so you can have 3D Shape base class declared like this:

public abstract class Shape3D extends Shape {
    protected int z;

    public Shape3D() {
        super();
    }
}

Not saying you will need a 3D object, but it's all about not entrapping yourself with design as the project gets more complex.


In your main class, you can have an array of Shapes and you can easily print them all knowing that they would have all defined the toString method.

public final class Main {
    public static void main(String []args) {
        List<Shape> shapes = new ArrayList<>();
        // fill the list with all kinds of shapes

        for (Shape shape: shapes) {
            System.out.println(shape); // Will default to System.out.println(shape.toString());
        }
    }
}

One last to to make note of; When you find yourself having to write long constructors for a class, you might want to ask yourself if you will benefit from making use of the Builder pattern. For example the Rectangle class can be created with a builder like:

public abstract class ShapeBuilder {
    int x, y;
    String color;

    public ShapeBuilder setX(int x) {
        this.x = x;
        return this;
    }

    public ShapeBuilder setY(int y) {
        this.y = y;
        return this;
    }

    public ShapeBuilder setColor(String color) {
        this.color = color;
        return this;
    }

    public abstract <T extends Shape> T build();
}

public class RectangleBuilder extends ShapeBuilder {
    int width;
    int height;

    public RectangleBuilder setWidth(int width) {
        this.width = width;
        return this;
    }

    ...

    @Override
    public Rectangle build() {
        Rectangle rect = new Rectangle(width, height, color);
        rect.setX(x);
        rect.setY(y);
        return rect;
    }
}
smac89
  • 39,374
  • 15
  • 132
  • 179
  • How would you create a colored `Rectangle` with that? You need more parameters in the constructor. Also, it's probably a bad idea to allow `Shape3D` to extend `Shape`, because polymorphism is meant to allow the user of an object to be unaware of the specific concrete class being used. It wouldn't make sense for a 3D object to be used in a 2D environment, so they should be separate entities. – 4castle Aug 29 '16 at 18:43