Home Assignment - Week 4 (OOP 2) : 0. Warm-Up: Shapes Hierarchy
Home Assignment - Week 4 (OOP 2) : 0. Warm-Up: Shapes Hierarchy
Try to complete the steps below in such a way to reuse the Point class and avoid any code
repetition:
b) Create a CircleShape class, extending Shape, which represents a circle of certain radius
- also override toString() for it, for convenience
- q: what fields should it have? constructors? any getters needed? other methods?
what can/should we use as access level for each?
could or should we make the class abstract? what about final?..
d) We want to position our shapes in 2D space, be able to move them and measure distances
between them.
- create an interface Movable, with methods:
- Point getCenter();
- void setCenter(Point center);
- void move(double deltaX, double deltaY) - should move the center point of the shape
adding deltaX/Y to its current x/y
- double distanceTo(Movable other) - the distance between the center of this shape and
center of other
- create 2 new classes, named Circle / Rectangle, which are similar to CircleShape /
RectangleShape, but also support the actions from Movable interface
- how can you reuse existing classes/interfaces to minimize the amount of new code to
write?
- did you need to change anything in existing classes/interface? (how much, and why)
- is there still any code duplication between circle/rectangle classes? can it be avoided?
e) Create a new class Square, which is similar to Rectangle, but always has the width the same
as height
- how can you reuse existing classes/interfaces to minimize the amount of code needed for
the new class? how many methods do you still need to write in Rectangle class?
- which of the existing classes/methods can we make final now? (respond before trying it in
code) would we have any reasons/benefits to do this?
g) If we would need to add a new Elipse shape in the future, where would you include it
in this class hierarchy? (should it be above/below Circle/CircleShape? why?)
(note: an Elipse is basically an elongate shape, and unlike a circle it has 2 differet radiuses -
see: https://www.math24.net/circle-ellipse )
h) Would it be a good idea to make Point itself movable (remove final, add setters or a move()
method)? Could we then remove the setCenter() from Movable? Can this cause any possible
problems later? (think about 2 shapes build using same center point, them moved.. what would
the distance between them be?)
i) Could we avoid somehow the need to repeat the declaration of the 'center' field (and
setter/getter for it) in each Shape?.. (hint: think about role/content of the Movable interfaces,
Shape class)
j) Optional: draw an UML class diagram for all these classes/interfaces, either on paper or
using an app.
(UML info: https://medium.com/@smagid_allThings/uml-class-diagrams-tutorial-step-by-step-520fd83b300b,
Online tools: https://www.draw.io/, https://online.visual-paradigm.com/solutions/free-class-diagram-tool/,
https://creately.com , Offline: https://wiki.gnome.org/Apps/Dia)
1. Circle & Cylinder (composition vs inheritance)
a. Define a Circle class (fields: centerX/Y,radius; methods: area(), length(), toString(),
getters)
- hint: circle area = PI*R^2, length = 2*PI*R ; may use Math.PI, Math.pow.. if
needed
- question: can you make the fields final? (can you think of any benefits it might
bring?) What about the methods and/or the class itself?
b. Based on it, define a CylinderH class, first using inheritance (no composition)
- with 2 constructors:
- Cylinder(double centerX, double centerY, double radius, double height)
- Cylinder(Circle circle, double height)
- Can you avoid repeating code between constructors? (hint: try to
use this())
- members: similar to Circle (inherited), but also with some extra fields (height) and
extra methods: volume(), getBase() (returns a Circle representing the base),
getHeight()
- hint: volume = base area * height
- question: for volume(), can you use the existing area() method?
- question: do all inherited methods make sense as they are for Cylinder?
(like: area(), length(), toString())
- now override methods to better fit Cylinder logic: area(), toString()
- hint: cylinder surface area = 2*base area + h*base perimeter
- question: can we use area() from Circle for computing area() for Cylinder,
instead of repeating that code? how?..
- question: after overriding area(), is the volume() still working correctly? if
not: why, and how can we fix it?
- question: can we reuse Circle.toString() when overriding toString() for
cylinder?
- question: what can we do with length(), as its really not relevant/needed anymore
for Cylinder? (hide it somehow? return some special value?..)
c. Now solve it again, but using composition (instead of inheritance) and creating a different
class named CylinderC (which should NOT extend Circle, but should instead contain a
Circle field…)
- question: does it look better/simpler, or worse that 1st? any pluses/minuses?
d. Define a Cylinder generic i nterface, and define in it the common properties of CylinderH
and CylinderC
- modify the 2 classes so that both of them implement this interface
- question: how many lines did you need to change in the 2 classes?
- question: does client code which wants to work with one of our 2 cylinder
implementations need to still mention the name of one of the 2 classes
(CylinderH/CylinderC), or can it use only Cylinder name everywhere? (if
class names are still needs, where exactly?)
e. Is it possible to further reduce duplication between the 2 classes? Is there any code
which you could move from the 2 cylinder implementation classes to the interface itself,
(reducing duplication) AND which would remain correct in the future, even if we add
other type of cylinder classes implementing this interface? (hint: see ‘default’ methods)
- is there any difference between the 2 classes, regarding how easy is to move
some of their code to the interface?..
- is any client code affected by such a move, what do you think?
- do all the tests still pass? (check after you did the moving)
2. Online Store
Write a class hierarchy for an online store. Please define and implement the following packages,
entities, fields and methods:
● customer package:
○ Customer class:
■ fields: firstName, lastName, cnp, address (this one should be of type
Address, see below)
○ Address class:
■ fields: street, streetNumber, town
● product package:
○ Product class:
■ fields: id, name, type (ex: clothing, electronics etc), price, color
● discount package:
○ Discount - class, abstract class or interface? it should allow the code to
somehow be able to apply a discount in a general way, and then support 2 types
of discounts (two implementations with compute the discount in different ways):
○ PercentageDiscount class
■ field: percent: what percentage of the price should be discounted (e.g 4%)
■ methods ?
○ FixedDiscount class
■ field: amount: a fixed amount (e.g 35 RON)
■ methods ?
Use inheritance to somehow define/support these two different discount types.
● (directly in top package of assignment)
○ Cart class:
■ fields: customer, list of products and discounts
■ methods:
● Cart(Customer customer) - constructor
● void addProduct(Product p)
● void removeProduct(Product p)
● void addDiscount(Discount d)
● void removeDiscount(Discount d)
● double computeProductsPrice() - compute total price of
products (without any discounts applied)
● double computeTotalPrice() - compute final cart price
(total price of products + all discounts applied)
● String g enerateInvoice() - should return a String (multi-line,
use “\n” to separate lines) representing the invoice for current cart;
it should contain this info:
○ Customer info
○ List of products
○ Total products price (without discounts)
○ List of discounts
○ Total amount (total products price + discounts)
Testing
Run the provided unit tests, and run some of your own (unit or manual)
Other Considerations
This hierarchy can be implemented and extended in many ways. Some suggestions are:
● Create a Store entity that holds all products and available discounts
● Both Discounts and Products can be added to the cart, so they could both implement a
common “CartItem” interface. Think about what it could contain (the common behavior
for Discounts and Products) and update your code accordingly.
● A Cart can hold products and discounts either in separate arrays (or ArrayList), or it can
simply use a single array/list of CartItems. Think about the advantages and
disadvantages of both approaches.
3. Complex Numbers
Create a Complex class that represents a complex number (more info:
https://en.wikipedia.org/wiki/Complex_number )
It should have the following methods:
- Complex add(Complex other) :adds this and other number and returns a new
Complex instance; hint: (a+bi) + (c+di) = (a+c) + (b+d)i
- Complex negate(): r eturns a new Complex number representing the negative value of
this; hint: if z = (a+bi) then -z = (-a-bi)
- Complex multiply(Complex other) :returns a new complex number that is equal
to this * other number; hint: (a+bi)(c+di) = (ac−bd) + (ad+bc)i
- boolean equals(Object other) : should return true only if ‘other’ is also an
instance of Complex class and the 2 numbers are equal (both their parts..)
- String toString() : should return strings such as “3 + 4i”
Testing:
Run the provided unit tests. May also run some tests of your own (manual or unit)
Optional:
4. MyList interface
Starting from the solved problems 6,7 from previous week (implementing MyArrayList and
MyLinkedList)
- define a new interface named MyList which contains the common behavior between
your 2 list implementations
- Question: what methods should you add to it?
- Change your 2 list classes so that they implement the new interface
- How many lines of code did you need to change? (more than 1?)
- Do all the tests for each list class still pass? (you did write your own unit tests for
them, didn’t you? :)...)
- Does any client code which wants to use one of your 2 list implementations need
to care/know which one is working one? (beside the very first step when it calls
the constructor of one or the other)
- Based on this, can you (re)write your tests in such a way that you test
both implementations by the same test code? (hint: in the test method you
will need some different code to create 2 different instances, one of
MyArrayList and one of MyLinkedList, but after this they can be tested by
exactly same code - meaning some assert…() methods grouped in a
single private method in test class, etc.. )
6. Other exercises
For more exercises/practice with OOP concepts, read and try to solve exercises from here,
especially the exercises 1.2 (Book and Author) and 5.1 (The Discount System):
http://swarm.cs.pub.ro/~vdobrota/oop/
You will see used there some diagrams, representing the structure of the Java classes, named
UML diagrams.
- They are used here mainly to show the structure of the classes (class name,
private/public fields and methods) and also some relations between them (extends/uses,
meaning ‘is a’ / ‘has a’).
- UML is a standard widely used in the industry, and it can be useful to be able to read
(even create) such diagrams.
For a short tutorial about UML, see:
https://medium.com/@smagid_allThings/uml-class-diagrams-tutorial-step-by-step-520fd83b300b