Programming 3
University of Alicante, 2024–2025
Fourth Programming Assignment
Polymorphism and inheritance
Relative weight of this assignment: 20% |
2D Shape s
Base project
Download the base Eclipse project for this assignment, which already contains some classes and unit tests. Import it as a Java project in Eclipse.
Introduction
In this practical assignment we will make use of concepts studied in units 6 and 7 (overwriting, covariance, abstract classes, etc.).
The aim is to implement the base model of a typical 2D geometric
drawing application, which allows us to create basic geometric figures.
The package es.ua.dlsi.prog3.p4.gui
, which is already
implemented, contains client code for the model that we must
implement, which will be found in the package
es.ua.dlsi.prog3.p4.model
.
When doing your implementation, keep in mind the different levels of visibility of each element (attributes and methods) as represented in the UML diagram. This is especially important when working with inheritance.
Here you have the UML diagram of these classes (the package
es.ua.dlsi.prog3.p4.gui
is omitted):
The Eclipse project already contains the classes from the
gui package and the Coordinate
class, as well as
some unit tests. At first, you will have compilation errors in the
gui package, this is normal, as it depends on the
model package; they will disappear as you implement the
latter.
The part of the package model that you must implement is the
hierarchy of classes made of Shape2D
and its subclasses,
AbstractPolygon
, Circle
,
Rectangle
and Square
, the factory class
Shape2DFactory
, in charge of creating new objects of the
hierarchy, and the class Canvas
that models a canvas on
which the figures will be drawn.
Package es.ua.dlsi.prog3.p4.model
(Remember that Coordinate
is already implemented.
You should not modify it.)
Coordinate
This class represents a two-dimensional coordinate. Three constants are defined in it:
- NOT_VALID : indicates an invalid numeric value for the components of a coordinate.
- MAX_VALUE : maximum positive value that the component of a coordinate can take (32767.9999).
- MIN_VALUE : negative minimum value that the component of a coordinate can take (-32767.9999).
Three constructors are available for Coordinate: the default constructor, the copy constructor and an overloaded constructor. The default constructor assigns invalid values to the coordinate components. The overloaded constructor allows constructing coordinates with given initial values, as long as they are within the range of maximum and minimum values allowed. If not, this constructor will throw the unchecked exception IllegalArgumentException.
There is also a pair of getters to get the coordinate component values, the toString() method to get a string representation of it and the equals() method that will return true when both coordinate components being compared are equal. In addition, we have the following two methods to operate on coordinates:
- sum(Coordinate) : it returns a new coordinate resulting from adding two other coordinates:
Coordinate c1, c2;
...
Coordinate c3 = c1.sum(c2);
- subtract(Coordinate) : works in the same way as the previous method, but returns a coordinate resulting from subtracting two other coordinates.
Both operations may produce coordinates with out-of-range values. In those cases they will throw the unchecked exception ArithmeticException.
Shape2D
This abstract class is the root of the class hierarchy by which we model two-dimensional figures. As you can tell from a quick glance at the UML diagram, the figures in our model have a position in the plane, specified by a Coordinate object; this property has its respective getter. In addition, these figures can be moved (move()), scaled (scale()) and cloned (clone()).
Constructors:
- Shape2D() : creates a figure with an invalid position.
- Shape2D(Coordinate) : creates a figure with the given position.
- Shape2D(Shape2D) : copy constructor.
Methods:
- getPosition() : getter to get the position of a figure.
- move(Coordinate) : moves a figure to the position given by the argument and returns the old position of the figure. If the argument is null it does not change the position and simply returns the current position.
- equals(Object) : returns true if the compared figures have equal positions.
- toString() : returns a string representing the figure with the following format:
(<x>,<y>)
where <x> and <y> are the components of the figure’s position. For example, for a figure at position (-30.4, 27.1):
(-30.4,27.1)
- clone() : abstract method to be implemented by subclasses of Shape2D that returns a copy of the figure.
- clone(Coordinate) : returns a copy of the figure, translated to the position specified by the argument.
- scale(double) : abstract method to be implemented by subclasses of Shape2D to scale the dimensions of a figure. The scaling percentage is specified by the argument, with respect to the current dimensions of the figure and can have any positive value greater than zero. For example, a value of 100 indicates keeping the dimensions of the figure unchanged. A value of 50 would indicate halving the dimensions and a value of 200 would indicate doubling the dimensions of the figure. This operation does not alter the position of a figure. It throws IllegalArgumentException if the specified percentage is negative or zero.
Circle
Subclass of Shape2D that models a circle. The centre of the circle corresponds to the position of the figure (in Shape2D) and its radius is specified by the radius attribute, which cannot be negative.
Constructors:
- Circle() : creates a circle of radius zero and no defined centre.
- Circle(Circle) : copy constructor
- Circle(Coordinate, double) : creates a circle with the centre and radius given as arguments. It throws IllegalArgumentException if the given radius is negative.
Methods:
- getRadius() : getter for radius.
- scale(double) : implements the scale() method of Shape2D. A circle is scaled by modifying its radius by the percentage indicated by the argument (see Shape2D.scale() for details).
- clone() : returns a copy of this circle.
- toString() : returns a string representing the state of the Circle object, in this format:
(<x>,<y>),radius=<r>
where <x> and <y> are the components of the figure’s position, and <r> is its radius value. For example, for a circle at position (-30.4, 27.1) with radius 3.5:
(-30.4,27.1),radius=3.5
- equals(Object) : returns true if the object passed as an argument is a circle and has the same centre and radius as this circle.
AbstractPolygon
This abstract class inherits from the Shape2D class and is the base class for all the polygons modelled in our application. The polygons in our model have, in addition to the attributes inherited from Shape2D, an angle indicating the rotation of the polygon; this property has its respective getter. Moreover, these figures can be rotated (rotate()). Note that although this class is abstract it does not define new abstract methods, but inherits the abstract methods of Shape2D and does not implement them.
Constructors:
- AbstractPolygon() : Creates a polygon with a rotation angle of 0 and undefined position.
- AbstractPolygon(Coordinate,double) : Creates a
polygon with the given position and rotation angle. It throws the
exception IllegalArgumentException if the
given angle is not in the range
[0.0,360.0)
. - AbstractPolygon(AbstractPolygon) : Copy constructor.
Methods:
- getAngle(): getter to get the rotation angle of a polygon.
- equals(Object): returns true if the polygons compared have equal positions and angles.
- toString(): returns a string representation of the polygon, in this format:
(<x>,<y>),angle=<angle>
where <x> and <y> are the components of the polygon’s position and <angle> its angle of rotation. For example, for a figure that is at position (-30.4, 27.1) and whose angle of rotation is 45.0°:
(-30.4,27.1),angle=45.0
- rotate(double): rotates a polygon by the number of
degrees given as an argument, whose value must be in the range
(-360.0,360.0)
. Otherwise the method throws an IllegalArgumentException. The rotation is performed by adding the angle received as an argument to the polygon’s angle. Note that the resulting rotation angle must be in the interval[0.0,360.0)
; so if, for example, the angle of the polygon is 45.0° and the rotation received as an argument is -90.0°, the resulting angle must be 315.0°.
Rectangle
Constructors:
- Rectangle() : creates a rectangle with a rotation angle, length and width equal to zero, and undefined position.
- Rectangle(Rectangle) : copy constructor
- Rectangle(Coordinate, double, double, double) : creates a rectangle with the position specified by the first argument, the rotation angle provided as second argument, and the length and width specified in the third and fourth arguments, respectively. The position of the rectangle is that of its upper left vertex. It throws IllegalArgumentException if the specified length or width is negative.
Methods:
- getLength() and getWidth() : getters for the length and width, respectively.
- scale(double) : implements the scale() method of Shape2D. A rectangle is scaled by modifying its length and width in the proportion indicated by the argument (see Shape2D.scale() for details).
- clone() : returns a copy of this rectangle.
- toString() : returns a string representing the state of the rectangle object, in this format:
(<x>,<y>),angle=<a>,length=<l>,width=<w>
where <x> and <y> are the position components of the figure, <a> is its roration angle, and <l> and <w> are the value of its length and width, respectively. For example, for a rectangle at position (-30.4, 27.1), with length 2.5 width 7.0 and angle 45.0°:
(-30.4,27.1),angle=45.0,length=2.5,width=7.0
- equals(Object) : returns true if the object passed as argument is a rectangle and has the same position, angle, length and width as this rectangle.
Square
Constructors:
- Square() : creates a square with a side equal to zero, a rotation angle equal to zero and an undefined position.
- Square(Square) : copy constructor
- Square(Coordinate, double, double) : creates a square with the position indicated by the first argument, the rotation angle indicated in the second argument and the side indicated as the last argument. The position of the square is that of its upper left vertex. It throws IllegalArgumentException if the indicated side is negative.
Methods:
- getSide(): getter for the side.
- scale(double) : implements the scale() method of Shape2D. A square is scaled by modifying its side in the proportion indicated by the argument (see Shape2D.scale() for details).
- clone() : returns a copy of this square.
- toString() : returns a string that represents the state of the square object, with this format:
(<x>,<y>),angle=<a>,side=<s>
where <x> and <y> are the components of the position of the figure, <a> is its angle of rotation and <s> is the value of its side. For example, for a square that is in position (-30.4, 27.1), with side 2.5 and angle 45.0°:
(-30.4,27.1),angle=45.0,side=2.5
- equals(Object) : returns true if the object passed as an argument is a square and has the same position, rotation angle and side as this square.
Canvas
Canvas represents a canvas containing two-dimensional geometric figures. A canvas has a title and dimensions (whose default value is DEFAULT_SIZE), specified by the width and height attributes. The canvas contains a list of figures of type Shape2D.
Constructors:
- Canvas() : creates a canvas without figures, with
default dimensions and title
"default canvas"
. - Canvas(Canvas) : copy constructor
- Canvas(String, double, double) creates an empty canvas with the title (first argument) and the width and height specified by the second and third arguments, respectively. It throws IllegalArgumentException if the specified width or height is negative.
Methods:
- addShape(Shape2D) : adds a figure to the canvas. It does not check whether the position of the figure is inside the canvas or not.
- clone() : returns a copy of this canvas.
- getWidth() and getHeight() : getters for the width and height of the canvas, respectively.
- getNumShapes() : returns the number of figures that have been added to the canvas.
- getShape(int) : returns the n-th figure added to the canvas, starting at one. It throws IndexOutOfBoundsException if the given index is not between 1 and the total number of figures added to the canvas.
- removeShape(int) : removes the figure added at n-th position to the canvas, starting at one. It throws IndexOutOfBoundsException if the given index is not between 1 and the total number of figures added to the canvas.
- toString() : returns a string with the title and dimensions of the canvas, the number of figures it contains, and then a line for each figure added to the canvas. For example, if we have a canvas with the title “Prog3”, of width 1024 and height 768, with two circles and a rectangle, this method will return a string like this:
Prog3 (1024.0,768.0) with 3 shapes
Shape2DFactory
Factory class whose responsibility is to create figures of the Shape2D hierarchy.
- createShape2D(String) : creates and returns the
requested figure using the default constructor of its class. The method
parameter shall contain the name of the figure class to create
(
Circle
,Square
orRectangle
). It throws IllegalArgumentException if the name of the figure is not Circle, Square or Rectangle.
Package es.ua.dlsi.prog3.p4.gui
This package contains two classes that you can use to test your implementation. Obviously, for it to work you must first complete your implementation. Both classes have a main() method, so you can run them separately.
The PaintApp class uses the Canvas class to create a canvas and add figures to it, the description of which will then be output to standard output.
The SwingPaint class is a small graphical application that allows you to draw geometric shapes on the screen using the mouse. First you must select the type of shape to draw (circle, rectangle or square) and then click and drag the mouse to draw it. Once you have shapes on the canvas, you can use the [Fill circles] or [Fill Rects & Sq.] buttons to fill/unfill all the circles, or all the rectangular shapes with the same color. You can also use the [Clear] button to clear all shapes from the canvas.
Unit tests
We provide unit tests in the test/
folder of the base
project that check the proper behaviour of the classes. It is important
that you understand what is tested and how it is done.
Documentation
This section will not be done in the control.
You must include in the source files all necessary comments in javadoc format. These comments must be defined at least for:
- Files: you must include name and id number (DNI, NIE, etc.) of the authors using the annotation @author.
- Classes: purpose of the class: at least 3 lines.
- Operations/methods: a description line for trivial functions, and a minimum of two lines for more complex functions. Input parameters, output parameters, and exceptions that are thrown by the method.
- Attributes: purpose of each attribute: at least 1 line.
You can use a non-javadoc comment when necessary.
It is not necessary to generate in HTML the javadoc documentation.
Minimal requirements for grading your assignment
- Your program must run with no errors.
- Unless otherwise stated, your program must not emit any kind of message or text through the standard output or standard error streams. Also avoid error output messages.
- The format of the name of all properties (public, protected and private) of classes must be strictly respected, both in terms of visibility scope and in terms of their name. Make sure that you respect the distinction between class and instance attributes, as well as the uppercase and lowercase letters in the identifiers.
- Your code must be conveniently documented and significant content has to be obtained after running the javadoc tool.
Submission of the assignment
The practice is delivered on the DLSI practice server.
You must upload a compressed file with your source code (only .java files). In a terminal, place yourself in the ‘src’ directory of your Eclipse project and enter the command
tar czvf prog3-p4.tgz *
This will compress all the code in src/, including those classes that were already implemented. This is correct and should be delivered as is.
Upload the file prog3-p4.tgz
to the practice server.
Follow the instructions on the page to log in and upload your work.
This delivery is only used to evaluate the documentation and to obtain the result of the oracle.
Grading
Testing of your assignment will be done automatically. This means that your program must strictly conform to the input and output formats given in this document, as well as to the public interfaces of all the classes: do not change the method signatures (name of the method, number, type and order of arguments, and data type returned) or their behaviour. So, for example, the Clase(int,int) method must have exactly two arguments of type int.
You can find more information about the grading of programming assignments in the subject description sheet.
In addition to the automatic grading, a plagiarism detection application will be used.
The applicable regulations of the University of Alicante Polytechnic School in the event of plagiarism are indicated below:
“Theoretical/practical work must be original. The detection of copy or plagiarism will suppose the qualification of”0” in the corresponding assignment. The corresponding Department and the University of Alicante Polytechnic School will be informed about the incident. Repeated conduct in this or any other subject will result in notification of the offences committed to the pertinent vice canchellor’s office so that they study the case and punish it in accordance with the legislation in force”.
Clarifications
- Although not recommended, you can add private attributes and methods to the classes. Notice, however, that you must implement ALL the methods indicated in this document and make sure that they work as expected, even if they are never called in your implementation.
- Any additional remark will be published in this page. It is recommended that you use this page as the primary source of the instructions.