forked from radams78/TDA553-lab1
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlab2reflection.txt
More file actions
68 lines (50 loc) · 6.31 KB
/
lab2reflection.txt
File metadata and controls
68 lines (50 loc) · 6.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
Your design probably has a type called something like Car or Vehicle that is a supertype to both Volvo and Scania.
What is the contract for gas() and brake() in this type? Do the implementations of gas() and brake() in both Volvo
and Scania follow this contract? Does your design follow the Liskov Substitution Principle?
We have an abstract class called Vehicle which is a superclass to our two other abstract classes called Car and Truck.
Car is then a superclass to Saab and Volvo while Truck is a superclass to CarHauler and Scania. We haven’t changed gas()
and break() at all since our implementation of the program requires the method move() to be used before the vehicle
actually moves. To get around the problem of move() being almost the same in Volvo and Scania, we placed a method move
in the superclass Vehicle. Weinherit that method into all subclasses of Vehicle and then, overrode it in Scania and
implemented the check to see if the platform was up or down and if the check passed, the move() method in Vehicle is called.
For Vehicles, the method brake() has the contract: Reduces the speed of the Vehicle (based on the amount in a span of 0 and
1) but it cannot reduce the speed of the Vehicle past 0. Similarly, the gas method() will increase the speed of the Vehicle
(based on the amount in a span of 0 and 1). The gas method cannot increase the speed to more than the car’s max speed(engine
power). The Scania and Volvo240 subclasses inherit from Truck and Car respectively, which, in turn, inherit from the superclass
Vehicle. Most methods that are relevant to the contracts for brake() and gas() are unchanged other than the speedFactor() method,
which is implemented differently for Scania and Volvo240. The difference only affects the max speed, which does not alter the
contract of gas or brake. The Volvo240 and Scania classes are subtypes to Vehicle and satisfy the same contracts, they are only
slightly stronger. The subclasses - and by extension - their objects of Vehicle follow the same or harsher contracts as Vehicle.
Therefore, our implementation and solution with the Superclass Vehicle and its subclasses follow the Liskov Substitution Principle.
How did you handle duplicated code between Scania, the car transporters and the repair shops? Explain how you decided when to use
subclassing and when to use composition and delegation.
Initially, we pondered whether a Scania could be retrofitted with a "car hauler(trailer)". We decided that a Scania dump truck and
a car transporter/car hauler are too different and even in the future, it’s incredibly unlikely that change type from one to the
other (I.e. become 1 class). Due to this, we chose to not have them represented through composition(/make use of delegation) from one
object Truck. Instead, we chose to create a common superclass for the two of them, called Truck (subclass of Vehicle) with their
common methods.
We also created an interface called IPlatform which is implemented by two different classes called ScaniaPlatform and CarHaulerRamp.
We created a layer of abstraction between the subclasses of Trucks and their platforms by allowing the superclass Truck to have an
attribute of type IPlatform. The classes Scania and CarHauler instantiate their specifically made platform objects in their constructor.
We implemented the platforms this way because the two truck’s platforms have quite a few differences in how they need to function while
having similar base functionality and contracts. With the future in mind (Open Closed Principle), along with the stated differences between
the two Platform, it made the most sense to inherit from an Interface. If in the future, other classes (I.e., more truck subclasses) are added,
they can implement the IPlatform interface if they are to have similar functionality. This same reason also holds for our interface CarHolder.
We decided to create the CarHolder interface for the similarities between CarHauler and CarRepairShop since many of the methods would do similar
things but all be implemented in slightly different ways. This also gives good extendibility the code in the future according to the open-closed
principle. It seems likely that this is a feature that could come to be used by other implementations. Then we implemented the required methods
in each of the classes. As the implementation of the methods do vary quite, no code is duplicated.
Did you need to modify the design from lab 1 in order to make these extensions? If so, how could you have noticed that your design from Lab 1 did not
follow the Open-Closed Principle before you started working on Lab 2?
We needed to modify our design/class structure a bit from lab 1 in order to incorporate trucks into the system. Previously, we only worked with 2
different types of cars, so it made perfect sense to have an abstract superclass called Car. However, when trucks were introduced into the system, we
realized that the implementation of the abstract class Car could be generalized to an abstract class called Vehicle with very few modifications. Then,
we added two intermediary classes, Car and Truck, to reduce code duplication; specifically, both trucks should have the same speedFactor method, so it
was extracted out to the Truck class. Having these classes also results in a more intuitive hierarchy and the ‘Is-A’ sanity check is always followed.
Furthermore, we decided that both Car and Truck should be declared as abstract classes as neither of them should be instantiated.
When we designed our code for Lab 1, we certainly didn’t follow the Open-Closed Principle to its fullest extent but while modifying the code for Lab 2,
apart from renaming the top-most abstract class, we made very few changes to the implementation of the class itself. This demonstrates that the Open-Closed
Principle was followed to a reasonable degree, but we certainly could have been more mindful of future extensions when designing our initial implementation.
Simply having a more open mind and actively thinking about how our system could be expanded upon could have led us to follow the Open-Closed Principle more
closely. However, it’s also important to keep in mind that although extensibility is certainly an advantage, it can be a bit impractical to design a system
in a way that avoids all potential modifications due to a change in the project scope.