It seems there are a lot of "empty containers" (I do not know the correct, technical term) with regards to multiple levels of abstract classes and functional interfaces with little or no code. Is this the case in real working situations?
Here's the class diagram:
And here's the code:
package shapeHierarchy;
public interface GetArea
{
double getArea();
}
package shapeHierarchy;
public interface GetVolume
{
double getVolume();
}
package shapeHierarchy;
public abstract class Shape implements GetArea
{
}
package shapeHierarchy;
public abstract class TwoDimensionalShape extends Shape
{
}
package shapeHierarchy;
public abstract class ThreeDimensionalShape extends Shape implements GetVolume
{
}
package shapeHierarchy;
public class Circle extends TwoDimensionalShape
{
private final double radius;
//constructor
public Circle(double radius)
{
//first subconstructor statement must call superclass constructor
//"chain up" here
this.radius = radius;
}
//accesor
public double getRadius()
{
return radius;
}
@Override
public String toString()
{
return String.format("%-10s%-10s%n%-10s%-10.1f%n",
"Shape:", "Circle", "Radius:", getRadius());
}
//fulfill compiler contract with interface
@Override
public double getArea()
{
return Math.PI * (getRadius() * getRadius());
}
}
package shapeHierarchy;
public class Triangle extends TwoDimensionalShape
{
private final double height;
private final double base;
//constructor
public Triangle(double height, double base)
{
//first subconstructor statement must call superclass constructor
//"chain up" here
this.height = height;
this.base = base;
}
//accesors
public double getHeight()
{
return height;
}
public double getBase()
{
return base;
}
@Override
public String toString()
{
return String.format("%-10s%-10s%n%-10s%-10.1f%n%-10s%-10.1f%n",
"Shape:", "Triangle", "Base:", getBase(), "Height:", getHeight());
}
//fulfill compiler contract with interface
@Override
public double getArea()
{
return getHeight() * (getBase() / 2);
}
}
package shapeHierarchy;
public class Square extends TwoDimensionalShape
{
private final double side;
//constructor
public Square(double side)
{
//first subconstructor statement must call superclass constructor
//"chain up" here
this.side = side;
}
//accesor
public double getSide()
{
return side;
}
@Override
public String toString()
{
return String.format("%-10s%-10s%n%-10s%-10.1f%n",
"Shape:", "Square", "Side:", getSide());
}
//fulfill compiler contract with interface
@Override
public double getArea()
{
return getSide() * getSide();
}
}
package shapeHierarchy;
public class Sphere extends ThreeDimensionalShape
{
private final double radius;
//constructor
public Sphere(double radius)
{
//first subconstructor statement must call superclass constructor
//"chain up" here
this.radius = radius;
}
//accesors
public double getRadius()
{
return radius;
}
@Override
public String toString()
{
return String.format("%-10s%-10s%n%-10s%-10.1f%n",
"Shape:", "Sphere", "Radius:", getRadius());
}
//fulfill compiler contract with interfaces
@Override
public double getArea()
{
return 4.0 * Math.PI * (getRadius() * getRadius());
}
@Override
public double getVolume()
{
return 4.0 * Math.PI * ((getRadius() * getRadius() * getRadius()) / 3.0);
}
}
package shapeHierarchy;
public class Cube extends ThreeDimensionalShape
{
private final double side;
//constructor
public Cube(double side)
{
//first subconstructor statement must call superclass constructor
//"chain up" here
this.side = side;
}
//accesors
public double getSide()
{
return side;
}
@Override
public String toString()
{
return String.format("%-10s%-10s%n%-10s%-10.1f%n",
"Shape:", "Cube", "Side:", getSide());
}
//fulfill compiler contract with interfaces
@Override
public double getArea()
{
return 6.0 * (getSide() * getSide());
}
@Override
public double getVolume()
{
return getSide() * getSide() * getSide();
}
}
package shapeHierarchy;
public class Tetrahedron extends ThreeDimensionalShape
{
private final double edge;
//constructor
public Tetrahedron(double edge)
{
//first subconstructor statement must call superclass constructor
//"chain up" here
this.edge = edge;
}
//accesors
public double getEdge()
{
return edge;
}
@Override
public String toString()
{
return String.format("%-10s%-10s%n%-10s%-10.1f%n",
"Shape:", "Tetrahedron", "Edge:", getEdge());
}
//fulfill compiler contract with interfaces
@Override
public double getArea()
{
return Math.sqrt(3.0) * (getEdge() * getEdge());
}
@Override
public double getVolume()
{
return (getEdge()*getEdge()*getEdge()) / (6.0 * Math.sqrt(2.0));
}
}
package shapeHierarchy;
public class ShapeTest
{
public static void main(String[] args)
{
//create an array of generic shape objects
Shape[] shapeObject = new Shape[6];
//populate array with specific, concrete shapes
shapeObject[0] = new Circle(9.0);
shapeObject[1] = new Triangle(5.0, 7.0);
shapeObject[2] = new Square(4.0);
shapeObject[3] = new Sphere(8.0);
shapeObject[4] = new Cube(6.0);
shapeObject[5] = new Tetrahedron(3.0);
//Ouput Header
System.out.println("Two and three dimensional objects processed polymorphically\n");
//generically process each shape object
for(Shape currentShape : shapeObject)
{
System.out.print(currentShape);
if (currentShape instanceof ThreeDimensionalShape)
{
ThreeDimensionalShape shape3d = (ThreeDimensionalShape) currentShape;
System.out.printf("%-10s%-10.1f%n",
"Area:", shape3d.getArea());
System.out.printf("%-10s%-10.1f%n",
"Volume:", shape3d.getVolume());
}
if (currentShape instanceof TwoDimensionalShape)
{
TwoDimensionalShape shape2d = (TwoDimensionalShape) currentShape;
System.out.printf("%-10s%-10.1f%n",
"Area:", shape2d.getArea());
}
System.out.println();
}
}
}