My struggles in understanding and learning about Object Oriented design, and the tools and knowledge I've taken from them.

Friday, August 21, 2009

Is-A Versus Has-A

Is-A versus Has-A

Believe it or not, the concept of whether a class "Is a [something]" or "Has a [something]" comes up quite a bit in object oriented design. For instance, consider the questions "Is a transmission a car?" and "Does a car have a transmission?"

Clearly, the second question is the only of the two that one would answer in the affirmative. A transmission is not a car, but a car does have a transmission. This is a classic "Is-A/Has-A" example. And sometimes the answer to the questions are not so clear cut, particularly if the relationship is more obscure, like the relationship between a wall and sheetrock. For example, sheetrock, if hanged, could perform the duties of a wall, and a wall can contain sheetrock. The implementation of the solution to that question could probably be solved with either implementing a "Is-A" paradigm, or a "Has-A" paradigm.

So, I haven't described how "Is-A" and "Has-A" are implemented in object oriented design. Well the answer to that is really quite simple:

"Is-A" is implemented via INHERITANCE
"Has-A" is implemented via COMPOSITION

INHERITANCE

Inheritance is a good solution when some component would be logically defined, more abstractly, as another object.

For instance, think of fruit. Fruit is really more of an abstract concept than it is an implementation. Fruit can either have seeds (raspberries), or not have seeds (bananas). It can also have rind (bananas), or not have rind (raspberries). These attributes (seeds, rinds) are values that the more abstract "fruit" has.

The implementation of this code could be as follows:

///Below is the "Fruit" class

public class Fruit
{
   private bool _hasSeeds;
   private bool _hasRind;

   ///Public attribute for whether or not the fruit has seeds
   public bool HasSeeds
   {
      get { return _hasSeeds; }
      set { _hasSeeds = value; }
   }

   ///Public attribute for whether the fruit has a rind
   public bool HasRind
   {
      get { return _hasRind; }
      set { _hasRind = value; }
   }   
}

///Public implementation of Banana
///Inherits from Fruit
public class Banana : Fruit
{
   ///Because the nature of a banana lends itself
   ///to knowing whether or not it has seeds and rind
   ///I will set those attributes in the constructor
   ///There is no reason that the class who calls
   ///Banana should have to know that a banana does not
   ///have seeds and does have a rind.
   public Banana()
   {
      this.HasSeeds = false;
      this.HasRind = true;
   }

   public void Peel()
   {
      ///Do some kind of action to peel the banana
      ///Since Peel() is not common among all fruits
      ///Peel() does not have to be in the Fruit class
      ///However, there may be reasons to put it in there
      ///anyway. It depends on how similar the behavior
      ///is for all fruits' Peel() behavior
   }
}

///Public implementation of Banana
///Inherits from Fruit
public class Raspberry : Fruit
{

   ///Like in Banana, raspberries HasSeeds and HasRind
   ///attributes are set
   public Raspberry()
   {
      this.HasSeeds = true;
      this.HasRind = false;
   }
}


As you can see, this example lends itself very well to the "Is-A" paradigm, because clearly, Bananas and Raspberries are "Fruit." Also note that in the above, I did not put the Peel() method in the Fruit class, because it didn't seem to belong there. If I have an issue where many fruits need to have the Peel() behavior defined in them, I could do one of the following:

1. Create an interface, and have any peelable fruit invoke it:

public interface IPeelableFruit
{
   ///Notice how there is no implementation in the interface's Peel()
   ///This is because an interface has no implementation
   ///It is simply a public contract between anyone implementing it
   void Peel();
}


Making an interface like the above would make a lot of sense if each fruit's implementation of Peel() were significantly different from one another.

With an interface, when I declare Banana, or any other peelable fruit, it would look like:

public class Banana : Fruit, IPeelableFruit
{
   public void Peel()...
}


2. I could create a PeelableFruit class, and a NonPeelableFruit class, and have subsequent classes inherit:

public class PeelableFruit : Fruit
{
   public PeelableFruit()
   {
      this.HasRind = true;
   }
}


Then declare Banana as follows:

public class Banana : PeelableFruit
{
...
}


What would happen in the above example is: PeelableFruit would be a fruit. Banana would be ("is-a") a PeelableFruit, and also a Fruit, because it would inherit from both. The technical term is "inheritance chain".

COMPOSITION

"Has-A" lends itself to object relationships where something contains something else, like the above example of a Car and a Transmission. But, suppose we wanted to really define, in a fruit, what a "Rind" is. This would give us a pretty good example of "Has-A", because a PeelableFruit "Has-A" Rind.

So, if I wanted to build a "Rind" class, I would do something like the following:


public class Rind
{
   private string _color;
   //Let's say this is how difficult the rind is
   //to peel
   //A banana would be something like 2, and an orange would
   //be 5
   private int _difficultyToPeel;

   private int _thicknessInMillimeters;

   ///Each of these private members would also have
   ///corresponding public members. I'll omit those

   public Rind(string color, int difficultyToPeel, int thicknessInMillimeters)
   {
      _color = color;
      _difficultyToPeel = difficultyToPeel;
      _thicknessInMillimeters = thicknessInMillimeters;
   }
}


So, now that the Rind class is defined, we can now put a Rind object into the PeelableFruit Class:


public class PeelableFruit : Fruit
{
   ///In the constructor, I instantiate the Rind object
   ///Otherwise, later on in the execution, I may forget
   ///and exceptions will get thrown
   ///Note the constructor had to be changed to facilitate
   ///the necessity to instantiate the
   ///Rind object (the color, difficulty, and thickness)
   ///parameters
   public PeelableFruit(string rindColor, int difficultyToPeel, int thicknessInMillimeters)
   {
      this.HasRind = true;
      this.PeelableFruitRind = new Rind(rindColor, difficultyToPeel, thicknessInMillimeters);
   }

   protected Rind PeelableFruitRind;
}


Then, when it comes time to instantiate a banana, doing so gives the banana much more robustness:

public class Banana : PeelableFruit
{
   ///I don't really know if this is "best practice" to make a class so knowing of how
   ///it implements a parent class, but I don't care. This is how I will continue
   ///to invoke this sort of inheritance
   public Banana() : base("YELLOW", 2, 7)
   {

   }
}


Note that, when Banana gets instantiated, it will inherit the PeelableFruit's protected variable PeelableFruitRind, and that fruit rind will have a color of "YELLOW", a peeling difficulty of 2, and a thickness of 7 millimeters. Now, Banana "Has-A" rind, and that means it's using COMPOSITION.

Post a Comment

Followers

Search This Blog

Powered by Blogger.