No collaboration is allowed on this program assignment. Your program must be an individual and original effort. Except for any situations explicitly identified in this assignment, if any, you may only receive help from your instructor or the tutors provided by the Computer Science Department. See the Syllabus for the significant consequences for disallowed collaboration and/or plagiarism.
Notice that no tests are required to be handed in for this assignment. You are, however, strongly encouraged to test - especially any methods whose correctness that you are less than sure about.
On one of the CSL servers and using handin (see How To Use handin, as necessary) hand in the following file(s) as specified:
File(s): Canvas.java, Circle.java, ConvexPolygon.java, Ellipse.java, Rectangle.java, Shape.java, Square.java, and Triangle.java
touser: eaugusti
subdirectory: 102-program04
You should develop and use your own tests prior to using the provided test driver. Do not use the provided test driver until your solution is complete and you believe it is correct or you are likely to be overwhelmed with error messages and will spend unnecessary time just trying to understand the test driver - a frustrating and inefficient way to approach problem solving with computers!
Using the save-as feature of your browser or wget/curl if you are a cool kid), not cut-and-paste, save P4TestDriver.java (to be published on the due date) in the same directory as your source files.
Compile the P4TestDriver.java with your solution and run P4TestDriver (see How to Compile and Run From the Command Line, as necessary). Remember that your code will be graded on one of the CSL servers so, to avoid unpleasant grading surprises, be sure to test on one of CSL servers before handing it in!
As with Program 3, I have provided a driver that lets you draw a canvas: Draw.java. It works the same as in Program 3 and supports all the shapes.
Objectives
To compare and contrast the features inheritance and interfaces.
To become familiar with abstract classes and methods.
To learn the difference between deep and shallow copies of objects.
To learn how to properly overriding the equals() method of the Object class.
To become familiar with the Comparable interface in the Java Standard Library.
You notice that rectangles and triangles are both special kinds of convex polygons and that, using inheritance, you can support the Rectangle and Triangle classes by making them each a subclass of ConvexPolygon. This will result in the Rectangle and Triangle classes having no instance variables and much of their logic moving the the ConvexPolygon class! While this does require some thought and effort it will result in more easily maintained code.
You decide that you would like to have a Square class. With
inheritance this should be relatively easy since,
geometrically speaking, squares are a special type of rectangle. You
decide to make the new Square class a subclass of
Rectangle and, because a square is a rectangle, no instance variables
are
required in the Square class since the Rectangle class has all of the
necessary data elements to
represent a square. However, there is the little problem of what to do
with the inherited setHeight and setWidth methods from
the Rectangle class - you don't want a Square object that isn't square!
In addition, you will have to handle the setVertex method inherited
from Rectangle (and, indirectly, ConvexPolygon).
As a new feature, you decide you want to be able to order shapes, i.e., determine if one shape is less than, equal to, or greater than another shape. You will base order to two criteria, first on the shapes name and, second, on its area. The Java Standard Library has an interface called Comparable that you will implement to solve this requirement. Know that the String class also implements the Comparable interface so it will be easy to compare the shape names.
Fianlly, you realize your Program 3 solution was vulnerable to having some of its instance variable data modfied by code outside the classes even though your instance variables were all private. This is because some of the instance variables are references to mutable objects and means anyone that has a reference to the same data as your private instance variables is able to modify the object your private reference points to. This is known as a shallow copy of an object. With the exception of Canvas, you will modify all of your code so that you store references to deep copies of all mutable objects and return references to deep copies of all mutable objects.
Suggestions
Specifications
Your source code must meet the Programming Guidelines.
Your solution must pass all test of the provided test driver (link and instruction provided below) when compiled and run on unix1, 2, 3, or 4.
Using the javadoc-style, document all public methods - including constructors - in the Canvas, Shape, and Square classes. See How to Write Javadoc Comments and How to Generate Javadoc Comments, as necessary).
Shape and all of its subclasses (not Canvas) must make deep-copies of all references to mutable objects that are passed in as parameters and used to modify instance variables or that go out as return values to instance variables.
Entirely remove the Shape interface (used in Program 3) from the Program 4 solution and replace it with a Java abstract class called Shape as follows:
Defines
the instance variables that are common to all of
its subclasses. Specifically, this is the color of the shape and its
filled state. Even though all shapes have a position, each class treats
position differently enough that storing that information in Shape
would complicate the code for a very small savings in memory usage. For
this reason we
will choose simplicity and readability over a small savings in memory
use.
Has a single
constructor that accepts one parameter for
each of the instance variables of the class - the order of parameters
is up to you.
Explictly specifies exactly the same methods as the Shape interface from Program 3 with exactly the same signatures (names, return types, and parameters). If a method can be wholly or partially implemented in this class then do so, otherwise include the method signature and make the method abstract. You declare an abstract method as follows:
Implements all the methods that have common behavior (partially or wholly) in all of the subclasses, i.e, Circle, Rectangle, Triangle, ConvexPolygon, Ellipse, and Square. Note that Ellipse and Square are new shape-types to be implemented in this assignment and are specified below.
Implements the java.lang.Comparable<T> interface. Implement the Comparable interface's compareTo method so that when comparing any two shapes, a.compareTo(b), the method would return as follows:
You can use the following code to obtain any object's name:
String
className =
yourObjectRef.getClass().getName();
Has the same single constructor signature as in Program 3 with appropriate logic changes to accommodate the use of inheritance.
This class must have the same methods and behaviors as its Program 3 equivalent. Due to the use of inheritance you'll need to do the following:
If necessary, override the inherited equals() method so that it returns true when the objects being compared are both the same type, are not equal to null, and all of their instance variables, defined here and in any super classes, have equal values for all instance variables. See How to Override the equals Method for assistance, as necessary.
Modify Rectangle so that it extends the ConvexPolygon class rather than implements the Shape interface.
Remove all instance variables from Rectangle - ConvexPolygon has all the necessary instance variables to support a Rectangle. Do not add width and height to ConvexPolygon but, instead, map the constructor parameters to fit the parameters of the ConvexPolygon class.
This class must have the same methods and behaviors (except setVertex as specified below) as its Program 3 equivalent. Due to the use of inheritance you'll need to do the following:
If necessary, override the inherited equals() method so that it returns true when the objects being compared are both the same type, are not equal to null, and all of their instance variables, defined here and in any super classes, have equal values for all instance variables. See How to Override the equals Method for assistance, as necessary.
Override the setVertex
method inherited from ConvexPolygon so that it throws an UnsupportedOperationExceptionwhen
it is called. This is to prevent someone from changing a Rectangle
object so that it no longer defines a rectilinear shape! While we could
solve this problem I feel it is a bit complicated and distracts from
the primary focus of the class and this assignment which is Object
Oriented Programming, not graphics!
Modify Triangle so that it extends the ConvexPolygon class rather than implements the Shape interface.
This
example assumes that Point references called a, b, and c have been
passed as
parameters to the Triangle constructor - change the names as necessary
in your implementation.
This class must have the same methods and behaviors as its Program 3 equivalent. Due to the use of inheritance you'll need to do the following:
Remove all methods from this class that are now inherited and are correct and appropriate as is.
If necessary, override the inherited equals() method so that it returns true when the objects being compared are both the same type, are not equal to null, and all of their instance variables, defined here and in any super classes, have equal values for all instance variables. See How to Override the equals Method for assistance, as necessary.
Has
the following single constructor: Square(int sideLength,
java.awt.Point position,
java.awt.Color color, boolean filled){...} Note that, like a Rectangle,
the position is considered to be the lower left corner of the shape.
Remember that a square has a length and width that are equal.
Since a square is, geometrically speaking, a type of rectangle, the Square class needs no explicit instance variables of its own and will, instead, make use of its super-class's instance variables.
Implements a method called getSize that has no parameters and a return type of int. The method returns the side length of the square (the value it was constructed with).
Implements a method called setSize that has a single parameter of type int that specifies the new side length for the sqaure object and a return type of void. The method changes the side length of the square to the specified size.
Overrides the setHeight and setWidthmethods inherited from Rectangle to make sure a square remains a square if someone changes its height or width. Whenever one of these methods is called on a Square object you must set both height and width to the new value without changing the position of the shape.
Implement a class called Ellipse so that it extends the abstract class Shape.
Has the following single constructor: Ellipse(double semiMajorAxis, double semiMinorAxis, java.awt.Point position, java.awt.Color color, boolean filled){...}. Note that the semi-major axis of an ellipse is the distance from the center to its furthest edge and the semi-minor axis is the distance to the closest edge. A circle has the same value for both (also known as the radius for a circle).
Implement only the instance variables unique to the class and inherits the others from the abstract Shape class.
Implement a method called setSemiMajorAxis
that has a single parameter of type double. The method sets the
ellipses major axis to the specified value. If the new value is less
than the current minor axis then the new value becomes the minor axis
value and the old (larger) minor axis value becomes the major axis
value.
Implement a method called getSemiMinorAxis that has no parameters and a return type of double. The method returns the minor axis of the ellipse.
If necessary, override the inherited equals() method so that it returns true when the objects being compared are both the same type, are not equal to null, and all of their instance variables, defined here and in any super classes, have equal values for all instance variables. See How to Override the equals Method for assistance, as necessary.
Modify Circle so that it extends Ellipse rather than implements the Shape interface.
Remove all instance
variables from Circle - Ellipse has all the necessary instance
variables to support a Circle. Remeber that a circle's radius is
both the major and minor axis value for an Ellipse.
Implement only the methods that must be implemented in the Circle class and inherits the others from Shape.
Overrides, as necessary, setSemiMajorAxis
so that a Circle object remains a circle shape, i.e., minor axis needs
to change too.
This class must have the same methods and behaviors as its Program 3 equivalent. Due to the use of inheritance you'll need to do the following:
Remove all methods from this class that are now inherited and are correct and appropriate as is.
The inherited setSemiMajorAxis and setSemiMinorAxis methods are interesting in this class - A circle's major and minor axis are always the same (a.k.a radius). You'll need to override these methods so that whenever either axis is changed the other is also set to the same value so that a circle remains a circle!
The Canvas
methods must have identical behavior to their Program
3
behavior. Note,
as a simplification to you, that no additional methods are to be written in
this class for the new shapes, Ellipse and Square. However, due
to inheritance and depending on how you wrote them, you may need to
modify some of the existing methods so that they work correctly. For
example, the method getRectangles is supposed to return all Rectangle
objects. Now, however, Square objects exist and they have an isA
relationship with the Rectangle class. You must now filter out Squares
in that method. Other methods have similar issues. You can use the instanceof operator with some
additional logic or you can call getClass()
to find out exactly what something is. There is a convienent
syntax for obtaining the Class class for any class, it is ClassName.class. You can compare
that with the value returned by getClass()
to see if the reference points to the specific class type you are
curious about.