Table of Contents
The main objective of this exercise is to review the use of inheritance in Java and its application to arrays of objects. Inheritance is based on the use of abstract
and extends
reserved identifiers. We will write a generic class and several specific subclasses that derive from the first. All abstract methods will be implemented and other methods will be overridden to show the use of polymorphism when working with objects from a class hierarchy. Finally, we will use an array of objects from the previous hierarchy that will allow us to practice with all inheritance concepts.
In this exercise, we will calculate the total area of a collection of figures that have been previously created. Starting from the class Triangle
, we will create a generic Figure
class that will allow us to make abstraction when working with any type of figures. Once the behaviour of the Figure
class has been defined, we will derive our Triangle
class from it and then we will adapt it in order that it can be considered as a Figure
. We will also create the Square
class that will inherit also from Figure
.
WARNING: the geometry of triangles and squares is a bit more complex to model of what may seem at a first look. If we try to solve them, you will loose most of the time dealing with such geometry problems, instead of the programming. Therefore, we are going to make two simplifications: one of them abou the area of the triangle and the other one about the shape of the squere. That is, this exercise is mathematically incorrect. Both simplifications will be explained in the corresponding section, but notice that in a real program, instead of an exercise like this one, you would need to use the right mathematics.
Once all figures are created, we will implement a class that contains an array of figures (for example, a triangle and a square) and then we will calculate the total area of them using class polymorphism to calculate the specific area of each one of them.
Figure
and Triangle
classesThe Figure
class represents a generic figure that will materialize later on a specific one (Triangle
, Square
, etc...). In Java, all this concept is represented by abstract classes that must be declared with the abstract
reserved keyword.
An abstract class is a class that declares one or more abstract methods whose implementation is done in the corresponding subclasses. Review the theory if you don't remember this concept.
The first task of the exercise is to define the following Figure
abstract class which represents a generic geometric figure:
public abstract class Figure { /** Name of the figure */ String name; /** Constructor of the figure with a name */ public Figure(String name) { } /** Calculates the area of a figure */ abstract public double area(); /** Indicates if the figure is regular or not */ abstract public boolean isRegular(); }
Start by downloading the following code
Figure.java
.
Notice that two methods have been declared as abstract, therefore they must be implemented in the corresponding subclasses.
Figure
class.Write the constructor of the class.
Does it make sense to implement the constructor of an abstract class? What for?
Triangle
.The Triangle
class now inherits from Figure
and you must modify it so that it reflects
such changes. To do so, start downloading the code listing of the Triangle
class from this link
Triangle.java
and make the following changes:
You will need to access the code of the Point
class to be able to compile the Triangle
class. You can download it from the following link Point.java
.
Declare the Triangle
class as follows:
public class Triangle extends Figure
Modify all constructors so that, at the first line of the code, all of them invoke to the base class constructor of
the Figure
class. This is made by using the reference super
.
Implement the following methods:
The area()
method that calculates the area of the triangle. The prototype
of the method is as follows:
public double area() { /* complete */ }
The triangle's area is calculated according to the following formula:
Area = ( base X height ) / 2
Use the distance(Point anotherPoint)
method, of the Point
class, with the vertexes of the triangle.
To calculate the height of any triangle it is necessary to apply some trigonometry: assume that the triangle is isosceles or equilateral and suppose that the projection of the opposite vertex is on the middle point of the base.
The isRegular()
method that allows to know whether a triangle is regular or not.
The prototype of the method is as follows:
public boolean isRegular() { /* complete */ }
The method must return true
if the length of all sides of the
triangle are equal.
You need only to compare the length of the three sides of the triangle.
The toString()
method that allows to obtain a text string representation
of the object. The prototype of the method is as follows:
public String toString() { /* complete */ }
The method must return a string of characters with the values of the triangle according to some format. The output format of the text string is shown below (data values shown are only examples) :
TRIANGLE [NAME=triangle1] [NON REGULAR] : VERTEXES (1.0, 1.0),(3.0, 1.0),(2.0, 2.0)
Please, remember that what has been shown above is a formatted text string with example values for attributes. They are only used to show you how the output format is, so in the implementation of the method you must not use these values, instead of you have to work with attributes directly.
Use the toString()
method of the vertexes of the triangle.
Remember that "+" operator, applied to text strings, allows to concatenate them.
main
method of the Triangle
classNow you must create a method to test the previous code. Create a main
method
in the Triangle
class that carries out the following tasks:
Create three points.
Create a triangle from those previous points.
Display on the console a descriptive text string with the values of the triangle.
Finally, print the value of the triangle's area.
Square
class.The Square
class inherits also from Figure
and has the following implementation:
public class Square extends Figure { /** Square vertexes */ private Point vertex1; private Point vertex2; private Point vertex3; private Point vertex4; /** Constructor with name and vertexes */ public Square(String name, Point diagonalVertex1, Point diagonalVertex3) { } /** Private method to calculate the vertexes for the other diagonal*/ private void otherDiagonal(Point vertex1, Point vertex3) { } /** Method implementation to calculate the area */ public double area() { return 0; } /** Implementation of the abstract method to calculate if the figure is regular. */ public boolean isRegular() { return false; } /** Returns a representative string of the square. */ public String toString() { return null; } }
Notice that a square can be created from one of its diagonals, therefore, the constructor receives
both vertexes of the diagonal as input parameters. To calculate the other two remaining vertexes, you have to
implement another private method called otherDiagonal(Point vertex1, Point vertex3)
that calculates and
creates the other two vertexes of the second square's diagonal from those you have passed as parameters to the method,
that is, it must calculate and create both vertexes vertex2
and vertex4
of the square.
Also notice that, as it derives from the Figure
abstract class, the Square
class
is forced to implement ALL abstract methods from Figure
... Which are those?
Square
class.Download the skeleton of the Square
class from this link
Square.java
and carry out the following tasks to implement the constructor of the class.
Firstly, implement the following private method that allows to create the vertexes of the second diagonal from the two vertexes of the first diagonal. The prototype of the method is as follows:
private void otherDiagonal(Point vertex1, Point vertex3) { /* complete */ }
This method creates both two vertexes of the second square's diagonal (vertex2
and
vertex4
) on the basis of the coordinates of the vertexes of the first diagonal that are passed as
input parameters.
This is one of the proposed simplifications. For a correct solution, you would need to find the vector which is perpendicular to the defined diagonal, and then find the point to the appropiate distance. However, in order to avoid you wasting the time dealing with mathmatics, we propose you to make the following simplification: just suppose that the sides are parallel to the axis, and therefore x and y f the new vertexes are just those of the provided vertexes, but with x and y interexchanged.
Implement the constructor of the class.
Use the method that you have implemented before to calculate the square's vertexes.
Square
class.Implement the methods of the Square
class that are specified next:
Write the area()
method that allows to calculate the area of a square.
The prototype of the method is as follows:
public double area() { /* complete */ }
The method calculates the area of a square according to the following formula: Area = base X altura
Use the distance(Point anotherPoint)
method of the Point
class with the vertexes of the square.
Write the isRegular()
method that allows to calculate whether a square is a
regular figure or not. The prototype of the method is as follows:
public boolean isRegular() { /* complete */ }
This method is obvious.
Write the toString()
method that allows to obtain a text representation of the object.
The prototype of the method is as follows:
public String toString() { /* complete */ }
The method must return a string of characters with the values of the square according to some kind of format. In this case, the output format of the text string is shown below (data values shown are only examples) :
SQUARE [NAME=square1] : VERTEXES (3.0, 3.0),(5.0, 3.0),(5.0, 5.0),(3.0, 5.0)
You can reuse most of the code of the toString()
method of the triangle.
Remember that "+" operator, applied to text strings, allows to concatenate them.
main
method of the Square
classNow you must create a method to test all the previous code. Create the main
method in the Square
class that must do the next tasks:
Create two points that will be the diagonal of your figure.
Create a square from those previous points.
Display on the console a descriptive text string with the values of the square.
Finally, print the value of the square's area.
The FiguresArea
class, which contains an array of figures, is the one that will allows us to calculate the total area of a collection of figures. It has the following definition:
public class FiguresArea { /** The array of figures */ private Figure figures[] = null; /** Constructor of the class with a fixed number of figures. */ public FiguresArea(int figuresNumber) { } /** Calculates the total area of the array figures. */ public double totalArea() { return 0.0; } /** Adds a new figure in the first empty position of the figures array. */ public void addFigure (Figure f){ } /** Prints a list with the array figures. */ public void print() { } /** Main Program */ public static void main(String args[]) throws Exception { } }
The constructor of the class initialises the array with the maximum number of figures.
The addFigure()
method allows to add an object of the Figure
class at the first free position
of the array. The totalArea()
method will add the respective areas of the figures that are contained in the array and
will return the total sum of them. The print()
method must print that total area and a descriptive message for every
figure on the console.
Download the FiguresArea
class from the following link FiguresArea.java
and carry out the following task:
Implement the constructor of the class that initialises the array of figures with the maximum number of them.
FiguresArea
class.Implement the methods of the FiguresArea
class that are specified next:
Write the addFigure (Figure f)
method that allows to add an object Figure
to
the array. The prototype of the method is as follows:
public void addFigure (Figure f) { /* complete */ }
The method must add a new object Figure
to the figure's array.
According to the theory, Figure
is abstract and, therefore there could not exist any object instances from that class. Object instances are, in fact, from the subclasses of Figure
, that is: Triangle, Square
.
Write the totalArea()
method that allows to calculate the total area of the figures
of the array.The prototype of the method is as follows:
public double totalArea() { /* complete */ }
The method must calculate the total area of the figures that are contained in the array of figures. To do so, it traverses the array adding the corresponding area of each figure of the array.
Remember what it the purpose of abstract methods of the Figure
class when implementing the method.
Write the print()
method that allows to print the total area of the figures. The prototype
of the method is as follows:
public void print() { /* complete */ }
The method prints on console the result of the calculation of the total area of the figures. Use the previous method to achieve it.
You can print also what figure is and to do so, use the toString()
method of each figure.
Polymorphism is a consequence of applying inheritance to classes and it allows what is known as method overriding by which, the same method can be implemented either at the base class or the subclass. At runtime, depending on the type of the object (which class the object actually instantiates), Java's interpreter will invoke to the one of the correspondig class.
main
method of the FiguresArea
classCreate the main program that has to carry out the following tasks:
Create an object of the FiguresArea
class with two figures as maximum.
Create a figure of the Triangle
class. To do so, you will have to create previously the
3 points that constitute the vertexes of the triangle and then pass them to the constructor of Triangle
.
Create a figure of the Square
class. To do so, you will have to create previously the
2 points that constitute diagonal of the square and then pass them to the constructor of Square
.
Add the triangle and the square to the array of figures of the FiguresArea
class. Use the
method you have implemented previously to do it.
Print the result of the calculation of the total area of the figures. To do so, invoke to the corresponding method of the class.
The solutions of this exercise are included in the following listings:
import java.io.BufferedReader; import java.io.InputStreamReader; /* * Figure.java * * (c) DIT-UC3M 2008 * */ public abstract class Figure { /** Name of the figure */ String name; /** Constructor of the figure with a name */ public Figure(String name) { this.name = name; } /** Calculates the area of a figure */ abstract public double area(); /** Indicates if the figure is regular or not */ abstract public boolean isRegular(); }
// Point class // (c) 2008 IT public class Point { private double x; private double y; // Constructor public Point(double x, double y) { this.x = x; this.y = y; } /** * Returns the coordinates of the point in a string */ public String toString() { // '+' operator in String objects does not mean mathematical sum, but // concatenation of strings return "(" + x + ", " + y + ")"; } /** * Returns the distance to the origin It can be also calculated calling to * distance(new Point(0,0)) */ public double distanceToOrigin() { return Math.sqrt(x * x + y * y); } /* Getter methods */ public double getX() { return x; } public double getY() { return y; } /** Returns the distance to another point */ public double distance(Point anotherPoint) { double x1; double y1; x1 = x - anotherPoint.getX(); y1 = y - anotherPoint.getY(); return Math.sqrt(x1 * x1 + y1 * y1); } /** Returns the quadrant */ public int quadrant() { if (x > 0.0 && y > 0.0) { return 1; } else if (x < 0.0 && y > 0.0) { return 2; } else if (x < 0.0 && y < 0.0) { return 3; } else if (x > 0.0 && y < 0.0) { return 4; } else { // (x==0.0 || y==0.0) return 0; } } /** * Returns the nearest point of the array in the parameter or null if array is * empty * * @param otherPoints * @return */ public Point nearest(Point[] otherPoints) { Point nearestPoint = null; double minDistance; double distance; if (otherPoints != null && otherPoints.length > 0) { // Start storing the first point in the array as the nearest one minDistance = distance(otherPoints[0]); nearestPoint = otherPoints[0]; for (int i = 1; i < otherPoints.length; i++) { // If nearer point is found, this is the new nearest one distance = distance(otherPoints[i]); if (distance < minDistance) { distance = minDistance; nearestPoint = otherPoints[i]; } } } return nearestPoint; } }
/** * Triangle.java * * (c) DIT-UC3M 2008 * */ public class Triangle extends Figure { /** Vertexes of the triangle */ private Point vertex1; private Point vertex2; private Point vertex3; /** Constructor of a triangle with their vertexes */ public Triangle(String name, Point vertex1, Point vertex2, Point vertex3) { super(name); this.vertex1 = vertex1; this.vertex2 = vertex2; this.vertex3 = vertex3; } /** Returns an array with the lenght of every edge of the triangle */ public double[] edgesLength() { double edgesLength[] = new double[3]; // Length 1->2 edgesLength[0] = vertex1.distance(vertex2); // Length 2->3 edgesLength[1] = vertex2.distance(vertex3); // Length 3->1 edgesLength[2] = vertex3.distance(vertex1); return edgesLength; } /** Implementation of the abstract method to calculate the triangle area. */ public double area() { // The area is ( base x height ) / 2 // We have to calculate the medium point from the base // to calculate the height // WARNNING: this an aproximation, the correct mathematical are as a bit // more complex. Point medium = new Point((vertex1.getX() + vertex2.getX()) / 2, (vertex1.getY() + vertex2.getY()) / 2); double base = vertex1.distance(vertex2); double height = medium.distance(vertex3); return (base * height) / 2; } /** * Implementation of the abstract method to indicate if the triangle is * regular. */ public boolean isRegular() { double edgesLength[] = edgesLength(); if (edgesLength[0] == edgesLength[1] && edgesLength[1] == edgesLength[2] && edgesLength[2] == edgesLength[0]) { return true; } return false; } /** Returns a representative string of the triangle. */ public String toString() { String s = "TRIANGLE"; s += " [NAME=" + name + "]"; if (isRegular()) { s += " [REGULAR] "; } else { s += " [IRREGULAR] "; } s += " : " + "VERTEXES " + vertex1.toString() + "," + vertex2.toString() + "," + vertex3.toString(); return s; } public static void main(String[] args) { Point p1 = new Point(1, 1); Point p2 = new Point(3, 1); Point p3 = new Point(2, 3); Triangle t = new Triangle("Triangle1", p1, p2, p3); System.out.println(t.toString()); System.out.println("Area: " + t.area()); } }
/* * Square.java * * (c) DIT-UC3M 2008 * */ public class Square extends Figure { /** Square vertexes */ private Point vertex1; private Point vertex2; private Point vertex3; private Point vertex4; /** Constructor with name and vertexes */ public Square(String name, Point diagonalVertex1, Point diagonalVertex3) { super(name); this.vertex1 = diagonalVertex1; this.vertex3 = diagonalVertex3; otherDiagonal(diagonalVertex1, diagonalVertex3); } /** Private method to calculate the vertexes for the other diagonal */ private void otherDiagonal(Point vertex1, Point vertex3) { vertex2 = new Point(vertex3.getX(), vertex1.getY()); vertex4 = new Point(vertex1.getX(), vertex3.getY()); } /** Method implementation to calculate the area */ public double area() { // In a square, the area is the base multiplied by the height double base = vertex1.distance(vertex2); double height = vertex1.distance(vertex4); return base * height; } /** * Implementation of the abstract method to calculate if the figure is * regular. */ public boolean isRegular() { // Due to our simplification, the square may not be actually a square, but a // rectangle. However, that is just our simplification, so we will return // always true: a square is always regular, by definition return true; } /** Returns a representative string of the square. */ public String toString() { String s = "SQUARE"; s += " [NAME=" + name + "]"; s += " : " + "VERTEXES " + vertex1.toString() + "," + vertex2.toString() + "," + vertex3.toString() + "," + vertex4.toString(); return s; } public static void main(String[] args) { Point p1 = new Point(1, 3); Point p2 = new Point(3, 1); Square s = new Square("Square1", p1, p2); System.out.println(s.toString()); System.out.println("Area: " + s.area()); } }
import java.io.BufferedReader; import java.io.InputStreamReader; /* * FiguresArea.java * * * (c) DIT-UC3M 2008 * */ public class FiguresArea { /** The array of figures */ private Figure figures[] = null; /** Constructor of the class with a fixed number of figures. */ public FiguresArea(int figuresNumber) { figures = new Figure[figuresNumber]; } /** Calculates the total area of the array figures. */ public double totalArea() { double totalArea = 0; for (int i = 0; i < figures.length; i++) { System.out.println(" Summing area of figure " + i + ":" + figures[i].area()); totalArea += figures[i].area(); } return totalArea; } /** Adds a new figure in the first empty position of the figures array. */ public void addFigure(Figure f) { for (int i = 0; i < figures.length; i++) { if (figures[i] == null) { figures[i] = f; break; } } } /** Prints a list with the array figures. */ public void print() { for (int i = 0; i < figures.length; i++) { /* * The call to the toString method works because * this method is defined in the Object class * (which all Java classes extend). * In general, we need to declare the method in the parent class * in order to be able to call it applying polymorphism. */ System.out.println(figures[i].toString()); } System.out.println("Total area: "+totalArea()); } /** Main Program */ public static void main(String args[]) throws Exception { FiguresArea figuresArea = new FiguresArea(2); Point p1 = new Point(1, 1); Point p2 = new Point(3, 1); Point p3 = new Point(2, 3); Triangle t = new Triangle("Triangle1", p1, p2, p3); Point p4 = new Point(1, 3); Point p5 = new Point(3, 1); Square s = new Square("Square1", p4, p5); figuresArea.addFigure(t); figuresArea.addFigure(s); figuresArea.print(); } }
The resolution of this exercise expects that the student learns the basics of references to a base class in inheritance hierarchies.
Download the following code listings
(
Position.java
,
Figure.java
,
Castle.java
,
Queen.java
)
and analyse both the class hierarchy and the Position
class that is going to be used.
We are going to deal with a set of figure elements. As Figure
class is abstract, we can't create instances from it, therefore, the collection of elements will be made up of objects from the Castle
and Queen
classes, indistinctly.
Implement the necessary code to hold, in the same collection and indistinctly, 2 objects of Queen
class and 4 objects of Castle
class.
Once the previous code has been implemented, traverse the collection invoking public void whoAmI()
method to test the correct operation of the base class references.
The solution is included in the following listing:
/** Testing class */ public class MainEn { public static int MAX_FIGURES = 6; // Testing method public static void main(String args[]) { Figure figures[] = new Figure[MAX_FIGURES]; // Objects are created figures[0] = new Queen(Figure.WHITE); figures[1] = new Castle(Figure.WHITE); figures[2] = new Castle(Figure.WHITE); figures[3] = new Queen(Figure.BLACK); figures[4] = new Castle(Figure.BLACK); figures[5] = new Castle(Figure.BLACK); // Go through the array with the use of references // to base class and polymorphism for (int i = 0; i < MAX_FIGURES; i++) { figures[i].whoAmI(); } } } // Main
Once this exercise has been finished, answer the following questions:
Which methods can be really invoked on the collection elements?
If Castle
class has implemented void castle()
method ("enrocar" in Spanish), could it be possible to invoke that method from a reference to the base class?
What should we have to do in order to be able to use the previous void castle()
method from an object of Castle
class that is pointed by a reference to Figure
class?
What should we do to know exactly to which class belongs every object pointed by a reference to the base class?
In this exercise we are going to review the main concepts of Object Oriented Programming (OOP) in Java with an application to create a class hierarchy to define geometric figures.
IMPORTANT NOTE: Remember that it is essential that you test your code whenever you implement any method, even though you are not asked to do so explicitly.
Figure
interface.In Java, an interface represents a kind of specification for all classes that implement it. Is is normally used to define a set of methods that we want to be provided by those classes.
For example, the
Comparable interface represents
objects that can be compared among them. It defines the compareTo()
method that allows to compare two objects. Every class whose objects want to be compared, as stated before, must implement this method. With an interface, further design and reuse of code is easily made since all objects share the same comparison method and the result of it.
Before going on to the next sections, try to answer the following questions:
What is an interface?
How is it implemented in Java?
What does it mean when a class implements an interface. How do you write this in Java?
All classes that are defined in this exercise must provide a basic set of methods, such as calculating the area of a geometric figure. In Java, this is achieved by defining the interface that declares all methods that must be implemented by the classes that implement it. For this exercise, every class that is created must implement the Figure
interface.
An abstract class is a class that can not be instantiated. This is shown by the use of the abstract
keyword in its declaration. An abstract class has "at least" one abstract method. That is, a method which is only declared but not implemented. This abstract method should be implemented by any subclass of the class. There is no point in instantiating objects of an abstract class and only subclasses of it can instantiate objects.
Remember that an abstract class can have constructors, though objects from it can not be instantiated.
Write the code of the GeometricFigure
abstract class, which must store the common information for all geometric figures (for example, a descriptive label) and has to provide all methods that can be shared among figures and are independent from the concrete shape of them. This class must implement the Figure
interface. Every class that can be represented by a geometric figure will inherit from GeometricFigure
class.
Every figure will have a descriptive label, so you should define an attribute of type String
to hold the text of the label.
Write a constructor that receives as input parameter the descriptive label of the figure.
Write the get
and set
methods that allow to modify the text label's attribute. As these methods should not be modified by anybody, declare them as final
to avoid any subclass to change the code of them (method overriding).
Implement the printDescription()
method. Notice that is not an abstract method although it invokes other abstract methods. Also, to avoid subclasses to change the code of it (method overriding), declare it as final
.
The method must print a text description of the figure on the console, including the label's text, the type of figure and its area, with the following format:
Tag: C-5 Figure Type: Square Area: 25
Remember that this class must implement the Figure
interface, so it must provide the code for the complete set of methods that have been defined in such interface. These methods should be implemented with the available information in the class, that is: getTag
and printDescription
methods. It is not necessary to include those methods which are impossible to be programmed in the class (Java assumes that they are implicitly abstract), but subclasses are responsible to provide their code implementations.
The next class to implement represents a rectangle and, obviously, its name will be Rectangle
. This class inherits from GeometricFigure
and implements the Figure
interface. A rectangle is defined by two dimensions, the base and the height and both are assumed to be integer values.
Write the class declaration and its correspondings attributes.
Write the constructor and the basic accesor get
and set
methods.
Write the following methods of the class:
public String getFigureType(); public double area(); public void drawTxt();
The drawTxt()
method must print the figure on the console. For example, a rectangle of base 6 and height 3 can be printed as follows:
****** ****** ******
Download the following code
FiguresTesting
and implement the code of the main
method to create, first, an instance of Rectangle
class and then print it on the console along with its description.
Answer the following questions:
Show the difference between a class and an object.
Which steps are involved in the instantiation's process of an object?
How is an object instantiated in Java?
REMEMBER: If a class implements an interface, automatically, all subclasses of it also implement that interface, even though this is not explicitly mentioned in its declaration. Notice that Rectangle
implements the Figure
interface though you haven't mentioned it in its declaration. That is because Rectangle
inherits from a class that implements Figure
.
A square is a type of rectangle where both base and height are equals. In Java, we can easily create a Square
class by deriving it from Rectangle
class.
Write the Square
class so that it inherits from the Rectangle
class of the previous section. Square
class just only needs a constructor which receives a single input parameter: the side value. The constructor will invoke the constructor of the Rectangle
superclass with the same value for the parameters of base and height
Test the new class by adding the necessary code to FiguresTesting
class.
Notice that, thanks to inheritance, it is possible to invoke methods of the Rectangle
superclass on an object of a derived type Square
without the need of programming them again.
Answer the following questions:
What is inheritance?
How do you express in Java that one class inherits from another?
Which methods of the superclass are visible from the subclasses?
What is the meaning of method overriding?
Remember that, opposite to the rest of the methods of a class, subclasses don't automatically inherit constructors from the superclass, but they can be invoked by the use of super()
keyword.
In this section, you must improve FiguresTesting
class to provide a user interface in text mode that allows the user to choose the figure that he/she wants to print on the console, then asks for the correct parameters for each figure, creates an instance of the correct class and, finally, prints the figure description and the figure itself on the console.
Use the following menu:
1.- Create rectangle 2.- Create square 3.- Display figure 0.- Exit
If the user selects one of the 2 first options, the program must ask him/her for the correct data. If he/she selects option 3, the last figure will be printed on the console (including its description). The menu must be shown until the user select the option Exit.
To simplify the generation of the correct object from the data entered by the user, it would be useful to add a readFigureData()
method to each class. This method would receive, as an input parameter, an object of type BufferedReader
from which, it will read the data introduced by the user. Then, it would return a correct instantiated object of the class with the entered data. Declare it as a static method.
Answer the following questions:
Which type are both instantiated objects (in options 1 and 2)?
Which type is the variable that references them?
Which methods from superclass are visible from the subclass?
Can you use the same variable as a reference for diferent types of figures? Why?
The solutions of this exercise are included in the following listings:
abstract class GeometricFigure implements Figure { private String tag; public GeometricFigure(String tag) { this.tag = tag; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } abstract public String getFigureType(); abstract public double area(); abstract public void drawTxt(); final public void printDescription() { System.out.println("Tag: " + getTag()); System.out.println("Figure Type: " + getFigureType()); System.out.println("Area: " + area()); // drawTxt(); } }
public interface Figure { public String getTag(); public String getFigureType(); public double area(); public void drawTxt(); public void printDescription(); }
import java.io.*; public class Rectangle extends GeometricFigure { private int base; private int height; public Rectangle(int base, int height) { this(base, height, "Rectangle"); } public Rectangle(int base, int height, String tag) { super(tag); this.base = base; this.height = height; } public int getBase() { return base; } public void setBase(int base) { this.base = base; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public String getFigureType() { return "Rectangle"; } public double area() { return (base * height); } public void drawTxt() { for (int row = 1; row <= height; row++) { // Print every row with * for (int col = 1; col <= base; col++) { System.out.print("*"); } // change of line System.out.println(); } } /** * Asks for the necessary data to define a rectangle and generates an object * with such data */ public static Rectangle readFigureData(BufferedReader entry) { Rectangle r = null; int base, height; String tag; try { System.out.print("Enter the base: "); System.out.flush(); base = Integer.parseInt(entry.readLine()); System.out.print("Enter the height: "); System.out.flush(); height = Integer.parseInt(entry.readLine()); System.out.print("Enter tag: "); System.out.flush(); tag = entry.readLine(); r = new Rectangle(base, height, tag); } catch (IOException ioe) { // Error (in/out): // A message is shown and null is returned System.err.println("Input/Output errora"); } catch (NumberFormatException nfe) { // Error (invalid data): // A message is shown and null is returned System.err.println("Invalid data error"); } return r; } }
import java.io.*; public class Square extends Rectangle { public Square(int edge) { this(edge, "Square"); } public Square(int edge, String tag) { super(edge, edge, tag); } public String getFigureType() { return "Square"; } /** * Asks for the necessary data to define a Square and generates an object with * such data */ public static Square readFigureData(BufferedReader entry) { Square c = null; String tag; int edge; try { System.out.print("Enter the edge: "); System.out.flush(); edge = Integer.parseInt(entry.readLine()); System.out.print("Enter the tag: "); System.out.flush(); tag = entry.readLine(); c = new Square(edge, tag); } catch (IOException ioe) { // Error (in/out): // A message is shown and null is returned System.err.println("Input/Output error"); } catch (NumberFormatException nfe) { // Error (invalid data): // A message is shown and null is returned System.err.println("Invalid data error"); } return c; } }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class FiguresTesting { /** * Basic testing: Generate and show figures * * Example of tests to see the classes */ private static void basicTesting() { Figure f; // Testing rectangle f = new Rectangle(10, 5); f.printDescription(); f.drawTxt(); System.out.println(); // To separate a figure from the other // testing Square f = new Square(4); f.printDescription(); f.drawTxt(); System.out.println(); } private static void showMenu() { System.out.println(); System.out.println("1. Create rectangle"); System.out.println("2. Create square"); System.out.println("3. See figure"); System.out.println("0. Exit"); System.out.print("Select an option > "); } public static void main(String args[]) { // Used for the first part of the test // basicTesting(); Figure f = null; String sOption; int iOption = 0; BufferedReader entry = new BufferedReader(new InputStreamReader(System.in)); // Program is executed till the user press 0-Exit" do { // Show the menu showMenu(); // Read the user option try { sOption = entry.readLine(); iOption = Integer.parseInt(sOption); } catch (IOException ioe) { // input/output error System.err.println("Input/Output error"); iOption = 0; // Finish } catch (NumberFormatException nfe) { // Error: Wrong option iOption = -1; } switch (iOption) { case 0: // Nothing, do-loop ends break; case 1: // Rectangle f = Rectangle.readFigureData(entry); break; case 2: // Square f = Square.readFigureData(entry); break; case 3: // see figure if (f != null) { f.drawTxt(); f.printDescription(); } else { System.out.println("Error: you must select a figure first"); } break; default: // error System.out.println("Wrong option"); } } while (iOption != 0); // Repeats till user select option 0-exit } }
This exercise tries to get an overall view of how Object Oriented Programmig allows us to reuse code, as long as we use it correctly.
Let's assume that we have a little knowledge about cypher theory with strings of characters. Let's say that we have heard of several cypher algorithms, most of them which are very difficult to implement. However, there is one called Caesar, which is based on character's rotation, that has a reasonable implementation.
Caesar's algorithm is based on the order of the alphabet characters. Each character of the input text string is replaced by an output character that is calculated by adding N positions to the letter on the alphabet. For example, if we find the 'A' character and the value of N is 3, the output character that will replace would be 'D'.
Implement an Object Oriented Programming solution that allows us to easily change this algorithm by another one which could be better in the future.
First of all, write a solution based on an abstract Cypher
class that models a generic encryption algorithm, with the basic encrypt and decrypt operations (that, for instance, receive a String and return a String), and then a CaesarCypher
class that inherits from the first one and implements the Cesar's algorithm.
Second, write a solution based on the definition of a Cryptable
interface and a CaesarCypher
class that implements it.
The solution using an abstract class (inheritance) is included in the following listing:
public abstract class Cypher { // Functions that define the encryption and decryption. abstract public String encrypt(String string); abstract public String decrypt(String string); /** * * @param args * [0] encrypt/decrypt * @param args * [1] string to encrypt/decrypt */ public static void main(String[] args) { if (args.length != 2) { System.out.println("Usage: java Cypher encrypt/decrypt [string]"); } else { Cypher cc = new CaesarCypher(); if (args[0].equals("encrypt")) { System.out.println(cc.encrypt(args[1])); } else if (args[0].equals("decrypt")) { System.out.println(cc.decrypt(args[1])); } else { System.out.println("Usage: java Cypher encrypt/decrypt [string]"); } } } // main } // Cypher /** * Specific implementation for a cypher, which replace each plain-text letter * with a fixed number of places down the alphabet. * Simple version ignoring punctuation marks. */ class CaesarCypher extends Cypher { /** Number of places which are going to be used for the replacement */ private static int CHARS_NUMBER_TO_SHIFT = 10; /** * Encrypts a string with the basic Caesar's cypher. * * @param string * String to encrypt * @return Encrypted String */ public String encrypt(String string) { String encryptedString = ""; for (int i = 0; i < string.length(); i++) { char letter = string.charAt(i); letter = (char) (letter + CHARS_NUMBER_TO_SHIFT); if (letter > 'z') { letter = (char) (letter - 'z' + 'a' -1); } encryptedString += letter; } return encryptedString; } // encrypt /** * Decrypts a string with the basic Caesar's cypher. * * @param string * String to decrypt * @return Decrypted String */ public String decrypt(String string) { String decryptedString = ""; for (int i = 0; i < string.length(); i++) { char letter = string.charAt(i); letter = (char) (letter - CHARS_NUMBER_TO_SHIFT); if (letter < 'a') { letter = (char) (letter + 'z' - 'a' + 1); } decryptedString += letter; } return decryptedString; } // decrypt } // CaesarCypher
The solution using the interface is included in the following listing:
public class Cypher { /** * * @param args * [0] encrypt/decrypt * @param args * [1] string to encrypt/decrypt */ public static void main(String[] args) { if (args.length != 2) System.out.println("Usage: java Cypher encrypt/decript [string]"); else { Cryptable cc = new CaesarCypher(); if (args[0].equals("encrypt")) { System.out.println(cc.encrypt(args[1])); } else if (args[0].equals("decrypt")) { System.out.println(cc.decrypt(args[1])); } else { System.out.println("Usage: java Cypher encrypt/decript [string]"); } } } // main } // Cifrado // Set of methods that specify the possibility of encrypt and decrypt in the // class that implements it. interface Cryptable { public String encrypt(String string); public String decrypt(String string); } /** * Implementation for a cypher, which replace each plain-text letter with a * fixed number of places down the alphabet */ class CaesarCypher implements Cryptable { /** Number of places which are going to be used for the replacement */ private static int CHARS_NUMBER_TO_SHIFT = 10; /** * Characters that can be used for the shift The characters ordering may be * changed in the array */ private static char characters[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '-', '.', '#', '@', '&', '%', '/', '(', '(', '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'W', 'X', 'Y', 'Z' }; /** * Given a character, this function returns its position in the array if it * exists, -1 otherwise * * @param character * @return */ private int searchCharPosition(char character) { int pos = -1; boolean found = false; int i = 0; while ((!found) && (i < characters.length)) { found = (characters[i] == character); i++; } if (found) { return (i - 1); } else { return pos; } } // searchCharPosition() /** * Replace the given character for the encryption. * * @param character * Char to replace * @return New (already replaced) character, which was CHARS_NUMBER_TO_SHIFT * positions down the alphabet */ private char encryptReplace(char character) { int pos = searchCharPosition(character); if (pos == -1) { return character; } else { int replacingCharPosition = 0; int newPosition = pos + CaesarCypher.CHARS_NUMBER_TO_SHIFT; if (newPosition <= characters.length - 1) { replacingCharPosition = newPosition; } else { replacingCharPosition = newPosition - characters.length; } return characters[replacingCharPosition]; } } // encryptReplace() /** * Replace the given character for the decryption. * * @param character * Char to replace * @return New (already replaced) character, which was CHARS_NUMBER_TO_SHIFT * positions up the alphabet */ private char decryptReplace(char character) { int pos = searchCharPosition(character); if (pos == -1) { return character; } else { int replacingCharPosition = 0; int newPosition = pos - CaesarCypher.CHARS_NUMBER_TO_SHIFT; if (newPosition >= 0) { replacingCharPosition = newPosition; } else { replacingCharPosition = characters.length - Math.abs(newPosition); } return characters[replacingCharPosition]; } } // decryptReplace() /** * Encrypts a string with the basic Caesar's cypher. * * @param string * String to encrypt * @return Encrypted String */ public String encrypt(String string) { String encryptedString = ""; for (int i = 0; i < string.length(); i++) { char letter = string.charAt(i); letter = encryptReplace(letter); encryptedString += letter; } return encryptedString; } // encrypt /** * Encrypts a string with the basic Caesar's cypher. * * @param string * String to decrypt * @return Decrypted String */ public String decrypt(String string) { String decryptedString = ""; for (int i = 0; i < string.length(); i++) { char letter = string.charAt(i); letter = decryptReplace(letter); decryptedString += letter; } return decryptedString; } // decrypt } // CifradoCesar