Problem (WorldCarSimulator)
A simulator which demonstrates the types of worldwide cars and their features. Users can choose any type of cars, then the simulator should give a demonstration of the type of car selected with the features available for that car.
Start with the basic design
Consider the following basic design:
Here Car is the base class. Taxi and RentedCar's are the derived classes.
Carry() is defined in base class and run() is overridden in the derived classes as required.
Suddenly you recognise that, as this is for worldwide cars, there are some cars, which go for race.
Only change required is to add a race() method in the base class so some cars can go for the race.
Update the code with race() method and now the code is ready for demo.
User starts the Demo, Some thing went horribly wrong.
"Taxi's started racing"
"RaceCar's started carrying passengers"
Now code needs to be modified.
Modify Taxi Class
Modify RaceCar Class
Consider about ToyCar they do not race nor they do not Carry.
Create the ToyCar Class
- Race() to do nothing
- Carry() to do nothing
As long as new type of car's come, the more modification is required. Let us think about some alternative solution like the following below.
In the above design, there is no reusability. As long as you add classes you need to override the methods carry() or race() etc. so this is not a good design.
Let us use Design Principle one:
"Identify the aspects of your application that vary and separate them from what stays the same"
Like this :
And one more thing we are using classes where in we do not have a chance for dynamic behaviour. Ie... for example Taxi started taking passengers and after some time it want to go for a race, which is not possible as per the last design.
Ok. Lets implement the second design principle:
"Program to an interface, not an implementation"
Let see the design now using the two principles:
We just had designed for the behaviour of Car's like Carry, Race etc..
Now we need to design the Types of Cars. It's now time to use the third principle.
"Favor Composition over inheritance"
Here is the final design:
At last we had implemented the first design pattern
"Strategy Pattern"
"Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it."
Now the code walk through:
Car Class
Public abstract class Car
{
CarryBehaviour carryBehaviour;
RaceBehaviour raceBehaviour;
public Car()
{
}
public abstract void run();
public void race()
{
raceBehaviour.race();
}
public void carry()
{
carryBehaviour.carry();
}
public void setraceBehaviour(RaceBehaviour rb)
{
raceBehaviour = rb;
}
public void setcarryBehaviour(CarryBehaviour cb)
{
carryBehaviour = cb;
}
}
RaceBehaviour (Interface), RaceCar (Class) & RaceNoWay (Class):
public interface RaceBehaviour
{
public void race();
}
public class RaceCar implements RaceBehaviour
{
public void race()
{
system.out.println("I am racing");
}
}
public class RaceNoWay implements RaceBehaviour
{
public void race()
{
system.out.println("I can't race");
}
}
CarBehaviour (Interface), CarryPeople (Class) , NonCarrier (Class) & CarryLoad (Class):
public interface CarryBehaviour
{
public void carry();
}
public class CarryPeople implements CarryBehaviour
{
public void carry()
{
system.out.println("I can carry only people");
}
}
public class NonCarrier implements CarryBehaviour
{
public void carry()
{
system.out.println("I can't carry");
}
}
public class CarryLoad implements CarryBehaviour
{
public void carry()
{
system.out.println("I can carry only Load");
}
}
Car Class
Public class Taxi extends Car
{
public Taxi()
{
carryBehaviour = new CarryPeople();
raceBehaviour = new RaceNoWay();
}
public void run()
{
System.out.println("Running a Taxi");
}
}
Jeep Class
Public class Jeep extends Car
{
public Taxi()
{
carryBehaviour = new CarryLoad();
raceBehaviour = new RaceNoWay();
}
public void run()
{
System.out.println("Running a Jeep");
}
}
RaceCar Class
Public class RaceCar extends Car
{
public Taxi()
{
carryBehaviour = new NonCarrier();
raceBehaviour = new RaceCar();
}
public void run()
{
System.out.println("Going for Race");
}
}
ToyCar Class
Public class ToyCar extends Car
{
public Taxi()
{
carryBehaviour = new NonCarrier();
raceBehaviour = new RaceNoWay();
}
public void run()
{
System.out.println("Play with ToyCar ");
}
}