Pr#2 Program to an Interface, not to an Implementation
We are back at the Linxemble Studios, discussing "who should fight using what"! You see two abstractions {Warrior, Weapon}. That's great! Having figured out abstractions is the key towards a successful design (Let's not discuss what's "successful" right here).
Let's look at this design:
As a usual design process, we have declared two interfaces- Warrior and Weapon. The idea is, each Warrior uses it's special Weapon. For instance, a Cavalier uses a Sword and an Archer uses a Bow. Both the Cavalier and the Archer implement the Warrior interface, while the Sword and the Bow implement the Weapon interface. To elaborate, both Cavalier and Archer implement their own set of methods (attack, getWeapon..etc.). Similarly, Both Sword and Bow implement their own "trigger" methods. Say, for the Sword the trigger means "one swipe of the word" and for the Bow it means "shooting the arrow".
Looks easy! We start implementing the specific classes like this:
So, things are implemented to some extent (in fact to quite a bit extent!), when another designer comes up with this fantastic idea of training! Here's what he has to say:
So, the obvious solution that comes to mind is extend the Cavalier (as it encapsulates the rest of the features, we just need to change the Weapon!).
But Arghhhhh! The TrainedCavalier class uses an instance of Sword. This is inflexible!
To be precise, the Cavalier instantiates its Weapon as:
...
public .... setWeapon()
{
... wp = new Sword();
...
}the TrainedCavalier could override it as:
So, the principle under the consideration "Program to an Interface, not to an Implementation"'s violated by the designer of Cavalier class, as he used the specific implementer (i.e, the Sword) of the Weapon abstraction. Now if one wants to refactor the Cavalier class (replacing the references to Sword with Weapon), he has to do so many changes in all the methods which call the methods on the Sword contained by the Cavalier. Current design of the Cavalier class done not make use of the Weapon abstraction well! Why, after all, did we force all the {Bow, Rifle, Sword} implement the interface Weapon?
Well, there is another bad thing going on, which requires us to peek into the actual code to understand it. Even after refactoring the Cavalier class, we'll see some repetition in the code across the Cavalier and Archer classes!
Well, for now, I'd say "You may as well use an Abstract Class!".
---- Discussion to be continued...
Looks easy! We start implementing the specific classes like this:
Let the Cavalier contain an instance of Sword and the Archer an instance of Bow.For now, let's just look at the Cavalier class. It contains the Sword.
So, things are implemented to some extent (in fact to quite a bit extent!), when another designer comes up with this fantastic idea of training! Here's what he has to say:
TrainedCavalier, that extends Cavalier. We need to make it contain Rifle instead of a Sword. This looks quite easy as the Rifle also is an implementer of the class Weapon and as this is an extension, we will have to override only a few methods (like attack, setWeapon) of the Cavalier, reusing the rest of the code! Isn't it?" "A Cavalier normally fights with a Sword. But he can be trained to be a rifle-horseman! Let's not touch the exesting Cavalier class, as it's instances still will be in use. Let's have another class
But Arghhhhh! The TrainedCavalier class uses an instance of Sword. This is inflexible!
To be precise, the Cavalier instantiates its Weapon as:
Sword swd ;Now the TrainedCavalier can NOT do this, while overriding the setWeapon method:
...
public .... setWeapon()
{
...
swd = new Sword();
...
}
public .... setWeapon()You can go ahead and suggest you solutions to Linxemble Studios. But let's think of what could have been a better start. Had the designer of the Cavalier class used this instead:
{
...
/* This is not possible, as "swd" is of type Sword,
* declared in the base class - Cavalier.
*/ swd = new Rifle();
...
}
Weapon wp;
...
public .... setWeapon()
{
... wp = new Sword();
...
}the TrainedCavalier could override it as:
public .... setWeapon()
{
...
/*
* This is valid, as "wp" is of type Weapon. Remember IS-A rule?
*/ wp = new Rifle();
...
}
So, the principle under the consideration "Program to an Interface, not to an Implementation"'s violated by the designer of Cavalier class, as he used the specific implementer (i.e, the Sword) of the Weapon abstraction. Now if one wants to refactor the Cavalier class (replacing the references to Sword with Weapon), he has to do so many changes in all the methods which call the methods on the Sword contained by the Cavalier. Current design of the Cavalier class done not make use of the Weapon abstraction well! Why, after all, did we force all the {Bow, Rifle, Sword} implement the interface Weapon?
Well, there is another bad thing going on, which requires us to peek into the actual code to understand it. Even after refactoring the Cavalier class, we'll see some repetition in the code across the Cavalier and Archer classes!
Well, for now, I'd say "You may as well use an Abstract Class!".
---- Discussion to be continued...