A Java Quadrilateral Inheritance Hierarchy - revisited











up vote
0
down vote

favorite












This is an exercise from Deitel&Deitel's "Java. How to Program (Early Objects)", 10th edition.



9.8 (Quadrilateral Inheritance Hierarchy) Write an inheritance hierarchy for classes Quadrilateral, Trapezoid, Parallelogram, Rectangle and Square. Use Quadrilateral as the superclass of the hierarchy. Create and use a Point class to represent the points in each shape. Make the hierarchy as deep (i.e., as many levels) as possible. Specify the instance variables and methods for each
class. The private instance variables of Quadrilateral should be the x-y coordinate pairs for the four endpoints of the Quadrilateral. Write a program that instantiates objects of your classes and outputs each object’s area (except Quadrilateral).



I've seen some realizations of this shape hierarchy on the Internet, but they impose additional restrictions on the orientation of quadrilaterals. For example, the bases of trapezoids/parallelograms are parallel to the X or Y axis etc. There are no indications of such restrictions in the task.



That's why I tried to implement this hierarchy using a simple custom Vector class. The Vectors are also used to work with points. This is a practice I've read about here.



package geometry2D;

import doubleWrapper.DoubleHandler;

public class Vector {
private double x;
private double y;

public Vector(double x, double y) {
setX(x);
setY(y);
}

public Vector(Vector vector) {
setX(vector.getX());
setY(vector.getY());
}

public Vector(Vector v1, Vector v2) {
setX(v2.getX() - v1.getX());
setY(v2.getY() - v1.getY());
}

public Vector clone() {
return new Vector(this);
}

public boolean equals(Vector vector) {
return DoubleHandler.compare(this.x, vector.x) == 0 &&
DoubleHandler.compare(this.y, vector.y) == 0;
}

public double length() {
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
}

public Vector toUnitVector() {
double length = this.length();
if (DoubleHandler.compare(length, 0.0) == 0)
throw new IllegalArgumentException("ERROR: undefined direction! A zero vector cannot be converted to a unit vector!");

return this.scale(1 / length);
}

public Vector scale(double scale) {
return new Vector(this.x * scale, this.y * scale);
}

public Vector add(Vector addend) {
return new Vector(this.x + addend.x, this.y + addend.y);
}

public Vector subtract(Vector subtrahend) {
return new Vector(this.x - subtrahend.x, this.y - subtrahend.y);
}

public static double dotProduct(Vector v1, Vector v2) {
return v1.x * v2.x + v1.y * v2.y;
}

public static double spanArea(Vector v1, Vector v2) {
return v1.x * v2.y - v1.y * v2.x;
}

public boolean isCollinear(Vector v1, Vector v2) {
v1.subtract(this);
v2.subtract(this);
return DoubleHandler.compare(spanArea(v1, v2), 0.0) == 0;
}

public Vector rotate(double angle) {
double cos = Math.cos(angle);
double sin = Math.sin(angle);
Vector rotateUnitX = new Vector(cos, sin);
Vector rotateUnitY = new Vector(-sin, cos);

return rotateUnitX.scale(this.x).add(
rotateUnitY.scale(this.y));
}

public void setX(double x) {
this.x = x;
}

public double getX() {
return x;
}

public void setY(double y) {
this.y = y;
}

public double getY() {
return y;
}
}


To compare doubles properly, I make use of this method for comparing doubles.



package doubleWrapper;

public class DoubleHandler {
private static final long BITS = 0xFFFFFFFFFFFFFFF0L;

public static int compare(double a, double b) {
long bitsA = Double.doubleToRawLongBits(a) & BITS;
long bitsB = Double.doubleToRawLongBits(b) & BITS;

if (bitsA < bitsB)
return -1;
if (bitsA > bitsB)
return 1;
return 0;
}
}


Quadrilateral class. There are no setters, so the objects of this class are immutable. All the subclasses are implemented in a similar fashion.
Additionally, the immutability solves the "Rectangle-Square" OOP problem (at least as I've understood).



package geometry2D;

public class Quadrilateral {
private Vector v0;
private Vector v1;
private Vector v2;
private Vector v3;

public Quadrilateral(Vector v0, Vector v1, Vector v2, Vector v3) {
if (v1.equals(v0) ||
v2.equals(v0) || v2.equals(v1) ||
v3.equals(v0) || v3.equals(v1) || v3.equals(v2))
throw new IllegalArgumentException("ERROR: two or more points coincide!");

if (v0.isCollinear(v1, v2)
|| v0.isCollinear(v1, v3)
|| v0.isCollinear(v2, v3)
|| v1.isCollinear(v2, v3))
throw new IllegalArgumentException(
"ERROR: at least three of the defined points are collinear!");

this.v0 = v0;
this.v1 = v1;
this.v2 = v2;
this.v3 = v3;
}

public Vector getV0() {
return v0;
}

public Vector getV1() {
return v1;
}

public Vector getV2() {
return v2;
}

public Vector getV3() {
return v3;
}

@Override
public String toString() {
return String.format("%s%n(%.3f, %.3f)%n(%.3f, %.3f)%n(%.3f, %.3f)%n(%.3f, %.3f)%n",
this.getClass().getSimpleName(),
v0.getX(), v0.getY(),
v1.getX(), v1.getY(),
v2.getX(), v2.getY(),
v3.getX(), v3.getY());
}
}


Trapezoid class.
The order of points in the constructor reflects the sequence in which they are connected in the quadrilateral.
It is assumed that:
v1 - v0 is a base of the trapezoid;
v2 - v0 is a lateral side;
length is the length of the remaining base.



The trapezoid's diagonal divides it into two triangles, and the area of the trapezoid is calculated by summing the areas of the triangles.
Using Vectors, it's very easy to find the area of a triangle using determinants (spanArea method).



package geometry2D;

public class Trapezoid extends Quadrilateral {
public Trapezoid(Vector v0, Vector v1, Vector v2, double length) {
super(v0, v1,
v2.add(new Vector(v0, v1).toUnitVector().scale(length)), v2);
if (length <= 0)
throw new IllegalArgumentException(
"ERROR: a triangle or a self-intersecting trapezoid!");
}

public double getArea() {
Vector base1 = new Vector(getV1(), getV0());
Vector side2 = new Vector(getV2(), getV1());
Vector base2 = new Vector(getV3(), getV2());
Vector side1 = new Vector(getV0(), getV3());

return 0.5 * (
Math.abs(Vector.spanArea(base1, side1)) +
Math.abs(Vector.spanArea(base2, side2)));
}
}


The Parallelogram, Rectangle and Square classes are easy to extend out one by one.



I'd like to hear any improvement suggestions on my realization.










share|improve this question


























    up vote
    0
    down vote

    favorite












    This is an exercise from Deitel&Deitel's "Java. How to Program (Early Objects)", 10th edition.



    9.8 (Quadrilateral Inheritance Hierarchy) Write an inheritance hierarchy for classes Quadrilateral, Trapezoid, Parallelogram, Rectangle and Square. Use Quadrilateral as the superclass of the hierarchy. Create and use a Point class to represent the points in each shape. Make the hierarchy as deep (i.e., as many levels) as possible. Specify the instance variables and methods for each
    class. The private instance variables of Quadrilateral should be the x-y coordinate pairs for the four endpoints of the Quadrilateral. Write a program that instantiates objects of your classes and outputs each object’s area (except Quadrilateral).



    I've seen some realizations of this shape hierarchy on the Internet, but they impose additional restrictions on the orientation of quadrilaterals. For example, the bases of trapezoids/parallelograms are parallel to the X or Y axis etc. There are no indications of such restrictions in the task.



    That's why I tried to implement this hierarchy using a simple custom Vector class. The Vectors are also used to work with points. This is a practice I've read about here.



    package geometry2D;

    import doubleWrapper.DoubleHandler;

    public class Vector {
    private double x;
    private double y;

    public Vector(double x, double y) {
    setX(x);
    setY(y);
    }

    public Vector(Vector vector) {
    setX(vector.getX());
    setY(vector.getY());
    }

    public Vector(Vector v1, Vector v2) {
    setX(v2.getX() - v1.getX());
    setY(v2.getY() - v1.getY());
    }

    public Vector clone() {
    return new Vector(this);
    }

    public boolean equals(Vector vector) {
    return DoubleHandler.compare(this.x, vector.x) == 0 &&
    DoubleHandler.compare(this.y, vector.y) == 0;
    }

    public double length() {
    return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
    }

    public Vector toUnitVector() {
    double length = this.length();
    if (DoubleHandler.compare(length, 0.0) == 0)
    throw new IllegalArgumentException("ERROR: undefined direction! A zero vector cannot be converted to a unit vector!");

    return this.scale(1 / length);
    }

    public Vector scale(double scale) {
    return new Vector(this.x * scale, this.y * scale);
    }

    public Vector add(Vector addend) {
    return new Vector(this.x + addend.x, this.y + addend.y);
    }

    public Vector subtract(Vector subtrahend) {
    return new Vector(this.x - subtrahend.x, this.y - subtrahend.y);
    }

    public static double dotProduct(Vector v1, Vector v2) {
    return v1.x * v2.x + v1.y * v2.y;
    }

    public static double spanArea(Vector v1, Vector v2) {
    return v1.x * v2.y - v1.y * v2.x;
    }

    public boolean isCollinear(Vector v1, Vector v2) {
    v1.subtract(this);
    v2.subtract(this);
    return DoubleHandler.compare(spanArea(v1, v2), 0.0) == 0;
    }

    public Vector rotate(double angle) {
    double cos = Math.cos(angle);
    double sin = Math.sin(angle);
    Vector rotateUnitX = new Vector(cos, sin);
    Vector rotateUnitY = new Vector(-sin, cos);

    return rotateUnitX.scale(this.x).add(
    rotateUnitY.scale(this.y));
    }

    public void setX(double x) {
    this.x = x;
    }

    public double getX() {
    return x;
    }

    public void setY(double y) {
    this.y = y;
    }

    public double getY() {
    return y;
    }
    }


    To compare doubles properly, I make use of this method for comparing doubles.



    package doubleWrapper;

    public class DoubleHandler {
    private static final long BITS = 0xFFFFFFFFFFFFFFF0L;

    public static int compare(double a, double b) {
    long bitsA = Double.doubleToRawLongBits(a) & BITS;
    long bitsB = Double.doubleToRawLongBits(b) & BITS;

    if (bitsA < bitsB)
    return -1;
    if (bitsA > bitsB)
    return 1;
    return 0;
    }
    }


    Quadrilateral class. There are no setters, so the objects of this class are immutable. All the subclasses are implemented in a similar fashion.
    Additionally, the immutability solves the "Rectangle-Square" OOP problem (at least as I've understood).



    package geometry2D;

    public class Quadrilateral {
    private Vector v0;
    private Vector v1;
    private Vector v2;
    private Vector v3;

    public Quadrilateral(Vector v0, Vector v1, Vector v2, Vector v3) {
    if (v1.equals(v0) ||
    v2.equals(v0) || v2.equals(v1) ||
    v3.equals(v0) || v3.equals(v1) || v3.equals(v2))
    throw new IllegalArgumentException("ERROR: two or more points coincide!");

    if (v0.isCollinear(v1, v2)
    || v0.isCollinear(v1, v3)
    || v0.isCollinear(v2, v3)
    || v1.isCollinear(v2, v3))
    throw new IllegalArgumentException(
    "ERROR: at least three of the defined points are collinear!");

    this.v0 = v0;
    this.v1 = v1;
    this.v2 = v2;
    this.v3 = v3;
    }

    public Vector getV0() {
    return v0;
    }

    public Vector getV1() {
    return v1;
    }

    public Vector getV2() {
    return v2;
    }

    public Vector getV3() {
    return v3;
    }

    @Override
    public String toString() {
    return String.format("%s%n(%.3f, %.3f)%n(%.3f, %.3f)%n(%.3f, %.3f)%n(%.3f, %.3f)%n",
    this.getClass().getSimpleName(),
    v0.getX(), v0.getY(),
    v1.getX(), v1.getY(),
    v2.getX(), v2.getY(),
    v3.getX(), v3.getY());
    }
    }


    Trapezoid class.
    The order of points in the constructor reflects the sequence in which they are connected in the quadrilateral.
    It is assumed that:
    v1 - v0 is a base of the trapezoid;
    v2 - v0 is a lateral side;
    length is the length of the remaining base.



    The trapezoid's diagonal divides it into two triangles, and the area of the trapezoid is calculated by summing the areas of the triangles.
    Using Vectors, it's very easy to find the area of a triangle using determinants (spanArea method).



    package geometry2D;

    public class Trapezoid extends Quadrilateral {
    public Trapezoid(Vector v0, Vector v1, Vector v2, double length) {
    super(v0, v1,
    v2.add(new Vector(v0, v1).toUnitVector().scale(length)), v2);
    if (length <= 0)
    throw new IllegalArgumentException(
    "ERROR: a triangle or a self-intersecting trapezoid!");
    }

    public double getArea() {
    Vector base1 = new Vector(getV1(), getV0());
    Vector side2 = new Vector(getV2(), getV1());
    Vector base2 = new Vector(getV3(), getV2());
    Vector side1 = new Vector(getV0(), getV3());

    return 0.5 * (
    Math.abs(Vector.spanArea(base1, side1)) +
    Math.abs(Vector.spanArea(base2, side2)));
    }
    }


    The Parallelogram, Rectangle and Square classes are easy to extend out one by one.



    I'd like to hear any improvement suggestions on my realization.










    share|improve this question
























      up vote
      0
      down vote

      favorite









      up vote
      0
      down vote

      favorite











      This is an exercise from Deitel&Deitel's "Java. How to Program (Early Objects)", 10th edition.



      9.8 (Quadrilateral Inheritance Hierarchy) Write an inheritance hierarchy for classes Quadrilateral, Trapezoid, Parallelogram, Rectangle and Square. Use Quadrilateral as the superclass of the hierarchy. Create and use a Point class to represent the points in each shape. Make the hierarchy as deep (i.e., as many levels) as possible. Specify the instance variables and methods for each
      class. The private instance variables of Quadrilateral should be the x-y coordinate pairs for the four endpoints of the Quadrilateral. Write a program that instantiates objects of your classes and outputs each object’s area (except Quadrilateral).



      I've seen some realizations of this shape hierarchy on the Internet, but they impose additional restrictions on the orientation of quadrilaterals. For example, the bases of trapezoids/parallelograms are parallel to the X or Y axis etc. There are no indications of such restrictions in the task.



      That's why I tried to implement this hierarchy using a simple custom Vector class. The Vectors are also used to work with points. This is a practice I've read about here.



      package geometry2D;

      import doubleWrapper.DoubleHandler;

      public class Vector {
      private double x;
      private double y;

      public Vector(double x, double y) {
      setX(x);
      setY(y);
      }

      public Vector(Vector vector) {
      setX(vector.getX());
      setY(vector.getY());
      }

      public Vector(Vector v1, Vector v2) {
      setX(v2.getX() - v1.getX());
      setY(v2.getY() - v1.getY());
      }

      public Vector clone() {
      return new Vector(this);
      }

      public boolean equals(Vector vector) {
      return DoubleHandler.compare(this.x, vector.x) == 0 &&
      DoubleHandler.compare(this.y, vector.y) == 0;
      }

      public double length() {
      return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
      }

      public Vector toUnitVector() {
      double length = this.length();
      if (DoubleHandler.compare(length, 0.0) == 0)
      throw new IllegalArgumentException("ERROR: undefined direction! A zero vector cannot be converted to a unit vector!");

      return this.scale(1 / length);
      }

      public Vector scale(double scale) {
      return new Vector(this.x * scale, this.y * scale);
      }

      public Vector add(Vector addend) {
      return new Vector(this.x + addend.x, this.y + addend.y);
      }

      public Vector subtract(Vector subtrahend) {
      return new Vector(this.x - subtrahend.x, this.y - subtrahend.y);
      }

      public static double dotProduct(Vector v1, Vector v2) {
      return v1.x * v2.x + v1.y * v2.y;
      }

      public static double spanArea(Vector v1, Vector v2) {
      return v1.x * v2.y - v1.y * v2.x;
      }

      public boolean isCollinear(Vector v1, Vector v2) {
      v1.subtract(this);
      v2.subtract(this);
      return DoubleHandler.compare(spanArea(v1, v2), 0.0) == 0;
      }

      public Vector rotate(double angle) {
      double cos = Math.cos(angle);
      double sin = Math.sin(angle);
      Vector rotateUnitX = new Vector(cos, sin);
      Vector rotateUnitY = new Vector(-sin, cos);

      return rotateUnitX.scale(this.x).add(
      rotateUnitY.scale(this.y));
      }

      public void setX(double x) {
      this.x = x;
      }

      public double getX() {
      return x;
      }

      public void setY(double y) {
      this.y = y;
      }

      public double getY() {
      return y;
      }
      }


      To compare doubles properly, I make use of this method for comparing doubles.



      package doubleWrapper;

      public class DoubleHandler {
      private static final long BITS = 0xFFFFFFFFFFFFFFF0L;

      public static int compare(double a, double b) {
      long bitsA = Double.doubleToRawLongBits(a) & BITS;
      long bitsB = Double.doubleToRawLongBits(b) & BITS;

      if (bitsA < bitsB)
      return -1;
      if (bitsA > bitsB)
      return 1;
      return 0;
      }
      }


      Quadrilateral class. There are no setters, so the objects of this class are immutable. All the subclasses are implemented in a similar fashion.
      Additionally, the immutability solves the "Rectangle-Square" OOP problem (at least as I've understood).



      package geometry2D;

      public class Quadrilateral {
      private Vector v0;
      private Vector v1;
      private Vector v2;
      private Vector v3;

      public Quadrilateral(Vector v0, Vector v1, Vector v2, Vector v3) {
      if (v1.equals(v0) ||
      v2.equals(v0) || v2.equals(v1) ||
      v3.equals(v0) || v3.equals(v1) || v3.equals(v2))
      throw new IllegalArgumentException("ERROR: two or more points coincide!");

      if (v0.isCollinear(v1, v2)
      || v0.isCollinear(v1, v3)
      || v0.isCollinear(v2, v3)
      || v1.isCollinear(v2, v3))
      throw new IllegalArgumentException(
      "ERROR: at least three of the defined points are collinear!");

      this.v0 = v0;
      this.v1 = v1;
      this.v2 = v2;
      this.v3 = v3;
      }

      public Vector getV0() {
      return v0;
      }

      public Vector getV1() {
      return v1;
      }

      public Vector getV2() {
      return v2;
      }

      public Vector getV3() {
      return v3;
      }

      @Override
      public String toString() {
      return String.format("%s%n(%.3f, %.3f)%n(%.3f, %.3f)%n(%.3f, %.3f)%n(%.3f, %.3f)%n",
      this.getClass().getSimpleName(),
      v0.getX(), v0.getY(),
      v1.getX(), v1.getY(),
      v2.getX(), v2.getY(),
      v3.getX(), v3.getY());
      }
      }


      Trapezoid class.
      The order of points in the constructor reflects the sequence in which they are connected in the quadrilateral.
      It is assumed that:
      v1 - v0 is a base of the trapezoid;
      v2 - v0 is a lateral side;
      length is the length of the remaining base.



      The trapezoid's diagonal divides it into two triangles, and the area of the trapezoid is calculated by summing the areas of the triangles.
      Using Vectors, it's very easy to find the area of a triangle using determinants (spanArea method).



      package geometry2D;

      public class Trapezoid extends Quadrilateral {
      public Trapezoid(Vector v0, Vector v1, Vector v2, double length) {
      super(v0, v1,
      v2.add(new Vector(v0, v1).toUnitVector().scale(length)), v2);
      if (length <= 0)
      throw new IllegalArgumentException(
      "ERROR: a triangle or a self-intersecting trapezoid!");
      }

      public double getArea() {
      Vector base1 = new Vector(getV1(), getV0());
      Vector side2 = new Vector(getV2(), getV1());
      Vector base2 = new Vector(getV3(), getV2());
      Vector side1 = new Vector(getV0(), getV3());

      return 0.5 * (
      Math.abs(Vector.spanArea(base1, side1)) +
      Math.abs(Vector.spanArea(base2, side2)));
      }
      }


      The Parallelogram, Rectangle and Square classes are easy to extend out one by one.



      I'd like to hear any improvement suggestions on my realization.










      share|improve this question













      This is an exercise from Deitel&Deitel's "Java. How to Program (Early Objects)", 10th edition.



      9.8 (Quadrilateral Inheritance Hierarchy) Write an inheritance hierarchy for classes Quadrilateral, Trapezoid, Parallelogram, Rectangle and Square. Use Quadrilateral as the superclass of the hierarchy. Create and use a Point class to represent the points in each shape. Make the hierarchy as deep (i.e., as many levels) as possible. Specify the instance variables and methods for each
      class. The private instance variables of Quadrilateral should be the x-y coordinate pairs for the four endpoints of the Quadrilateral. Write a program that instantiates objects of your classes and outputs each object’s area (except Quadrilateral).



      I've seen some realizations of this shape hierarchy on the Internet, but they impose additional restrictions on the orientation of quadrilaterals. For example, the bases of trapezoids/parallelograms are parallel to the X or Y axis etc. There are no indications of such restrictions in the task.



      That's why I tried to implement this hierarchy using a simple custom Vector class. The Vectors are also used to work with points. This is a practice I've read about here.



      package geometry2D;

      import doubleWrapper.DoubleHandler;

      public class Vector {
      private double x;
      private double y;

      public Vector(double x, double y) {
      setX(x);
      setY(y);
      }

      public Vector(Vector vector) {
      setX(vector.getX());
      setY(vector.getY());
      }

      public Vector(Vector v1, Vector v2) {
      setX(v2.getX() - v1.getX());
      setY(v2.getY() - v1.getY());
      }

      public Vector clone() {
      return new Vector(this);
      }

      public boolean equals(Vector vector) {
      return DoubleHandler.compare(this.x, vector.x) == 0 &&
      DoubleHandler.compare(this.y, vector.y) == 0;
      }

      public double length() {
      return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
      }

      public Vector toUnitVector() {
      double length = this.length();
      if (DoubleHandler.compare(length, 0.0) == 0)
      throw new IllegalArgumentException("ERROR: undefined direction! A zero vector cannot be converted to a unit vector!");

      return this.scale(1 / length);
      }

      public Vector scale(double scale) {
      return new Vector(this.x * scale, this.y * scale);
      }

      public Vector add(Vector addend) {
      return new Vector(this.x + addend.x, this.y + addend.y);
      }

      public Vector subtract(Vector subtrahend) {
      return new Vector(this.x - subtrahend.x, this.y - subtrahend.y);
      }

      public static double dotProduct(Vector v1, Vector v2) {
      return v1.x * v2.x + v1.y * v2.y;
      }

      public static double spanArea(Vector v1, Vector v2) {
      return v1.x * v2.y - v1.y * v2.x;
      }

      public boolean isCollinear(Vector v1, Vector v2) {
      v1.subtract(this);
      v2.subtract(this);
      return DoubleHandler.compare(spanArea(v1, v2), 0.0) == 0;
      }

      public Vector rotate(double angle) {
      double cos = Math.cos(angle);
      double sin = Math.sin(angle);
      Vector rotateUnitX = new Vector(cos, sin);
      Vector rotateUnitY = new Vector(-sin, cos);

      return rotateUnitX.scale(this.x).add(
      rotateUnitY.scale(this.y));
      }

      public void setX(double x) {
      this.x = x;
      }

      public double getX() {
      return x;
      }

      public void setY(double y) {
      this.y = y;
      }

      public double getY() {
      return y;
      }
      }


      To compare doubles properly, I make use of this method for comparing doubles.



      package doubleWrapper;

      public class DoubleHandler {
      private static final long BITS = 0xFFFFFFFFFFFFFFF0L;

      public static int compare(double a, double b) {
      long bitsA = Double.doubleToRawLongBits(a) & BITS;
      long bitsB = Double.doubleToRawLongBits(b) & BITS;

      if (bitsA < bitsB)
      return -1;
      if (bitsA > bitsB)
      return 1;
      return 0;
      }
      }


      Quadrilateral class. There are no setters, so the objects of this class are immutable. All the subclasses are implemented in a similar fashion.
      Additionally, the immutability solves the "Rectangle-Square" OOP problem (at least as I've understood).



      package geometry2D;

      public class Quadrilateral {
      private Vector v0;
      private Vector v1;
      private Vector v2;
      private Vector v3;

      public Quadrilateral(Vector v0, Vector v1, Vector v2, Vector v3) {
      if (v1.equals(v0) ||
      v2.equals(v0) || v2.equals(v1) ||
      v3.equals(v0) || v3.equals(v1) || v3.equals(v2))
      throw new IllegalArgumentException("ERROR: two or more points coincide!");

      if (v0.isCollinear(v1, v2)
      || v0.isCollinear(v1, v3)
      || v0.isCollinear(v2, v3)
      || v1.isCollinear(v2, v3))
      throw new IllegalArgumentException(
      "ERROR: at least three of the defined points are collinear!");

      this.v0 = v0;
      this.v1 = v1;
      this.v2 = v2;
      this.v3 = v3;
      }

      public Vector getV0() {
      return v0;
      }

      public Vector getV1() {
      return v1;
      }

      public Vector getV2() {
      return v2;
      }

      public Vector getV3() {
      return v3;
      }

      @Override
      public String toString() {
      return String.format("%s%n(%.3f, %.3f)%n(%.3f, %.3f)%n(%.3f, %.3f)%n(%.3f, %.3f)%n",
      this.getClass().getSimpleName(),
      v0.getX(), v0.getY(),
      v1.getX(), v1.getY(),
      v2.getX(), v2.getY(),
      v3.getX(), v3.getY());
      }
      }


      Trapezoid class.
      The order of points in the constructor reflects the sequence in which they are connected in the quadrilateral.
      It is assumed that:
      v1 - v0 is a base of the trapezoid;
      v2 - v0 is a lateral side;
      length is the length of the remaining base.



      The trapezoid's diagonal divides it into two triangles, and the area of the trapezoid is calculated by summing the areas of the triangles.
      Using Vectors, it's very easy to find the area of a triangle using determinants (spanArea method).



      package geometry2D;

      public class Trapezoid extends Quadrilateral {
      public Trapezoid(Vector v0, Vector v1, Vector v2, double length) {
      super(v0, v1,
      v2.add(new Vector(v0, v1).toUnitVector().scale(length)), v2);
      if (length <= 0)
      throw new IllegalArgumentException(
      "ERROR: a triangle or a self-intersecting trapezoid!");
      }

      public double getArea() {
      Vector base1 = new Vector(getV1(), getV0());
      Vector side2 = new Vector(getV2(), getV1());
      Vector base2 = new Vector(getV3(), getV2());
      Vector side1 = new Vector(getV0(), getV3());

      return 0.5 * (
      Math.abs(Vector.spanArea(base1, side1)) +
      Math.abs(Vector.spanArea(base2, side2)));
      }
      }


      The Parallelogram, Rectangle and Square classes are easy to extend out one by one.



      I'd like to hear any improvement suggestions on my realization.







      java inheritance






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked yesterday









      Alex Konrad

      935




      935






















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          0
          down vote













          1) Your Quadrilateral class and its subclasses aren't immutable right now...
          Having no setters doesn't make an objects immutable. Any fields that are mutable themselves and are not hidden nor cloned when returned make the whole object mutable.



          An example with your code :



          new Square(vector).getV0().setY(17); // I just made the square... not a square anymore


          You have to make your Vector class immutable as well by getting rid of the setters.



          If I were you, I'd also make every field final.



          2) Please note that double (and float) are fairly odd fellas. They have a special value named NaN (used to represents some erroneous results such as 0/0) that can (and will) really mess up your calculations. You should check against it in the Vector constructor with this method : https://docs.oracle.com/javase/10/docs/api/java/lang/Double.html#isNaN(double)



          3) You should also reimplement the hashCode method as, when you override equals you should override the hashCode as well as can be seen in the Object javadoc : https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#hashCode()



          4) The isCollinear method does not work as intended right now... don't forget to unit test your code ;)



          5) When you override the clone method, you should also implements the Cloneable interface. However, I'd recommend not implementing it (and thus removing the method) as, firstly, there is usually little point in cloning an immutable object and, secondly, you already have a copy constructor.



          6) I don't really get why you need to use this special method for your Double comparison.... if you want high precision with real number... double are simply not meant for you...



          7) You can't use the getArea method on objects of type 'Quadrilateral' which I find odd



          8) Finally, please note that the package name doesn't respect the conventions (it should start with a DNS suffix such as 'fr' or 'com').



          Overall, the code is fairly clear and easy to read so that's a good point.






          share|improve this answer





















            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209154%2fa-java-quadrilateral-inheritance-hierarchy-revisited%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            0
            down vote













            1) Your Quadrilateral class and its subclasses aren't immutable right now...
            Having no setters doesn't make an objects immutable. Any fields that are mutable themselves and are not hidden nor cloned when returned make the whole object mutable.



            An example with your code :



            new Square(vector).getV0().setY(17); // I just made the square... not a square anymore


            You have to make your Vector class immutable as well by getting rid of the setters.



            If I were you, I'd also make every field final.



            2) Please note that double (and float) are fairly odd fellas. They have a special value named NaN (used to represents some erroneous results such as 0/0) that can (and will) really mess up your calculations. You should check against it in the Vector constructor with this method : https://docs.oracle.com/javase/10/docs/api/java/lang/Double.html#isNaN(double)



            3) You should also reimplement the hashCode method as, when you override equals you should override the hashCode as well as can be seen in the Object javadoc : https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#hashCode()



            4) The isCollinear method does not work as intended right now... don't forget to unit test your code ;)



            5) When you override the clone method, you should also implements the Cloneable interface. However, I'd recommend not implementing it (and thus removing the method) as, firstly, there is usually little point in cloning an immutable object and, secondly, you already have a copy constructor.



            6) I don't really get why you need to use this special method for your Double comparison.... if you want high precision with real number... double are simply not meant for you...



            7) You can't use the getArea method on objects of type 'Quadrilateral' which I find odd



            8) Finally, please note that the package name doesn't respect the conventions (it should start with a DNS suffix such as 'fr' or 'com').



            Overall, the code is fairly clear and easy to read so that's a good point.






            share|improve this answer

























              up vote
              0
              down vote













              1) Your Quadrilateral class and its subclasses aren't immutable right now...
              Having no setters doesn't make an objects immutable. Any fields that are mutable themselves and are not hidden nor cloned when returned make the whole object mutable.



              An example with your code :



              new Square(vector).getV0().setY(17); // I just made the square... not a square anymore


              You have to make your Vector class immutable as well by getting rid of the setters.



              If I were you, I'd also make every field final.



              2) Please note that double (and float) are fairly odd fellas. They have a special value named NaN (used to represents some erroneous results such as 0/0) that can (and will) really mess up your calculations. You should check against it in the Vector constructor with this method : https://docs.oracle.com/javase/10/docs/api/java/lang/Double.html#isNaN(double)



              3) You should also reimplement the hashCode method as, when you override equals you should override the hashCode as well as can be seen in the Object javadoc : https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#hashCode()



              4) The isCollinear method does not work as intended right now... don't forget to unit test your code ;)



              5) When you override the clone method, you should also implements the Cloneable interface. However, I'd recommend not implementing it (and thus removing the method) as, firstly, there is usually little point in cloning an immutable object and, secondly, you already have a copy constructor.



              6) I don't really get why you need to use this special method for your Double comparison.... if you want high precision with real number... double are simply not meant for you...



              7) You can't use the getArea method on objects of type 'Quadrilateral' which I find odd



              8) Finally, please note that the package name doesn't respect the conventions (it should start with a DNS suffix such as 'fr' or 'com').



              Overall, the code is fairly clear and easy to read so that's a good point.






              share|improve this answer























                up vote
                0
                down vote










                up vote
                0
                down vote









                1) Your Quadrilateral class and its subclasses aren't immutable right now...
                Having no setters doesn't make an objects immutable. Any fields that are mutable themselves and are not hidden nor cloned when returned make the whole object mutable.



                An example with your code :



                new Square(vector).getV0().setY(17); // I just made the square... not a square anymore


                You have to make your Vector class immutable as well by getting rid of the setters.



                If I were you, I'd also make every field final.



                2) Please note that double (and float) are fairly odd fellas. They have a special value named NaN (used to represents some erroneous results such as 0/0) that can (and will) really mess up your calculations. You should check against it in the Vector constructor with this method : https://docs.oracle.com/javase/10/docs/api/java/lang/Double.html#isNaN(double)



                3) You should also reimplement the hashCode method as, when you override equals you should override the hashCode as well as can be seen in the Object javadoc : https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#hashCode()



                4) The isCollinear method does not work as intended right now... don't forget to unit test your code ;)



                5) When you override the clone method, you should also implements the Cloneable interface. However, I'd recommend not implementing it (and thus removing the method) as, firstly, there is usually little point in cloning an immutable object and, secondly, you already have a copy constructor.



                6) I don't really get why you need to use this special method for your Double comparison.... if you want high precision with real number... double are simply not meant for you...



                7) You can't use the getArea method on objects of type 'Quadrilateral' which I find odd



                8) Finally, please note that the package name doesn't respect the conventions (it should start with a DNS suffix such as 'fr' or 'com').



                Overall, the code is fairly clear and easy to read so that's a good point.






                share|improve this answer












                1) Your Quadrilateral class and its subclasses aren't immutable right now...
                Having no setters doesn't make an objects immutable. Any fields that are mutable themselves and are not hidden nor cloned when returned make the whole object mutable.



                An example with your code :



                new Square(vector).getV0().setY(17); // I just made the square... not a square anymore


                You have to make your Vector class immutable as well by getting rid of the setters.



                If I were you, I'd also make every field final.



                2) Please note that double (and float) are fairly odd fellas. They have a special value named NaN (used to represents some erroneous results such as 0/0) that can (and will) really mess up your calculations. You should check against it in the Vector constructor with this method : https://docs.oracle.com/javase/10/docs/api/java/lang/Double.html#isNaN(double)



                3) You should also reimplement the hashCode method as, when you override equals you should override the hashCode as well as can be seen in the Object javadoc : https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#hashCode()



                4) The isCollinear method does not work as intended right now... don't forget to unit test your code ;)



                5) When you override the clone method, you should also implements the Cloneable interface. However, I'd recommend not implementing it (and thus removing the method) as, firstly, there is usually little point in cloning an immutable object and, secondly, you already have a copy constructor.



                6) I don't really get why you need to use this special method for your Double comparison.... if you want high precision with real number... double are simply not meant for you...



                7) You can't use the getArea method on objects of type 'Quadrilateral' which I find odd



                8) Finally, please note that the package name doesn't respect the conventions (it should start with a DNS suffix such as 'fr' or 'com').



                Overall, the code is fairly clear and easy to read so that's a good point.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered yesterday









                Ronan Dhellemmes

                1,227111




                1,227111






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.





                    Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                    Please pay close attention to the following guidance:


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209154%2fa-java-quadrilateral-inheritance-hierarchy-revisited%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Mont Emei

                    Province de Neuquén

                    Journaliste