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

Saturday, August 29, 2009

Many-To-One Composition Relationships

Many-to-one relationships are very common, especially in applications involving databases. The reason for this is that the many-to-one paradigm is a common scenario in the real world, and programming languages, databases, and other technical constructs attempt to represent the real world.

Consider the relationship between a car and its tires. Composition solves the problem of how a car "has" tires. It would go something like this:


///Here is a generic implementation of a Car class
public class Car
{
   private List _allFourTires;

   public Car()
   {
      _allFourTires = new List();
   }
}

///And here is the class "Tire", which composes Car
public class Tire
{
   private string _model;

   private string _type;

   private int _width;

   private double _aspectRatio;

   ///You get the idea
}


The List<> type is a great way to solve the problem of many-to-one composition, but there is another way in C#, and it is called "Collections."

Collections are implemented in a lot of the base .Net objects, including DataSets and DataTables.

For instance, consider the below code:


///Assume I have a DataSet called myDataSet, and it has
///a bunch of datatables in it
foreach(DataTable table in myDataSet.Tables)
{
   foreach(DataRow row in table.Rows)
   {
      ///Do some stuff with the table
   }
}


The reason DataSet has a .Tables and a DataTable has .Rows is because they implement the IEnumerable .Net interface.

As mentioned above, an alternative to implementing the IEnumerable is to have a DataSet have a List<> variable with DataTables, and to have a DataTable have a List<> variable of DataRows, but over time, the IEnumerable is easier to manage.

Interfaces are a public contract between classes. They're basically a grocery list, and if you plan on using them, you must have everything on the grocery list. Luckily, IEnumerable has only one "item" on the list. That item is a public method called GetEnumerator().

When implementing a paradigm such as this, the effect you're really having is to create a bridge between the container class (Car), and the containee class (Tire). So, in between Car and Tire goes the IEnumerable class. In the below example, we'll call it TireCollection


/// The container class
public class Car
{
   ///A global variable of the bridge (IEnumerable) class
   private TireCollection _tireCollection;

   /// Constructor - makes a tire collection based
   /// on the model
   public Car(string model)
   {
       makeTireCollection(model);
   }

   /// This is just sort of a factory method - I could
   /// get more abstract, and create a TireCreatorFactory
   /// class, but I think this is sufficient for this example
   private void makeTireCollection(string model)
   {
       if (model == "Ford")
          makeFordTireCollection();
   }

   /// Makes the tire collection for Ford cars
   private void makeFordTireCollection()
   {
       List tireList = new List();
       tireList.Add(new Tire("Bridgestone", "P", 215, 75));
       tireList.Add(new Tire("Bridgestone", "P", 215, 75));
       tireList.Add(new Tire("Bridgestone", "P", 215, 75));
       tireList.Add(new Tire("Bridgestone", "P", 215, 75));
       _tireCollection = new TireCollection(tireList);
   }

   /// This is an unnecessary attribute, but if I didn't have this
   /// I would have to do:
   /// foreach(Tire in car)...
   /// With this attribute, I can do
   /// foreach(Tire in car.Tires)
   /// I like that better
   public TireCollection Tires
   {
       get { return _tireCollection; }
   }
}


Notice in the above, I have an attribute called Tires. As mentioned in the comments, this is an unnecessary attribute, but I think it looks cleaner when we go to implement the Car class.

Below is the IEnumerable class TireCollection. Note that it has a method called GetEnumerator():


/// This essentially acts as a bridge between
/// the Car class and the tire class
public class TireCollection : IEnumerable
{
   ///We do have a List<> variable in the TireCollection.
   ///So, in effect, we don't get away from having a List<>
   ///variable...the collection simply buys us a "decoupling"
   ///effect
   List _tireList;

   public TireCollection(List tireList)
   {
       _tireList = tireList;
   }

   public IEnumerator GetEnumerator()
   {
       return (_tireList as IEnumerable).GetEnumerator();
   }
}


Then, the Tire class looks pretty typical


/// This is the Tire class - there are many tires on a car
/// Well...four, anyway
public class Tire
{
   private string _model;

   private string _type;

   private int _width;

   private double _aspectRatio;

   /// Constructor
   public Tire(string model, string type, int width, double aspectRatio)
   {
       _model = model;
       _type = type;
       _width = width;
       _aspectRatio = aspectRatio;
   }

   /// This is simply a public method
   public string GetTireProperties()
   {
       return "Tire is of model " + _model + " with type " + _type;
   }
}


Now comes the question of how to implement this code. This part is the simple part:


Car car = new Car("Ford");
foreach (Tire tire in car.Tires)
{
   ///Output tire.GetTireProperties()
}


It is a few more steps to implement an IEnumerable interface, but it's worth it for those solutions that wish to have looser coupling between the composition relationship.

In the comments in the makeFordTireCollection() method in Car, I mention that I could have just as easily implemented a TireCreatorFactory class that assembles a Tire collection for me. The effect of doing so would have provided a higher degree of decoupling between Car and Tire, which is probably a more optimal situation, since loosely coupled class interactions are highly valued in object oriented design.

No comments:

Followers

Search This Blog

Powered by Blogger.