C# Composition


This entry is part 2 of 10 in the series C# Inheritance

There are two kinds of relationships between classes:

  • Inheritance (is-a)
  • Composition (has-a)

This post is based on an instructional video by Mosh Hamedani at Udemy.com. Suppose we are building an application that has many parts to it. One of the parts, a logger, is responsible for logging information to a text file. We have two other parts of the application that need to use the logger. We have a DbMigrator and we have an Installer. Both of these have a relationship to the logger class. They both use it. This is nothing new here in C#. We don’t need any special syntax like we need with inheritance. One class contains another. We can say that a car has an engine. The engine is inside the car.

Generally, developers favour composition over inheritance. For an example of inheritance we have the post called C# Interfaces Extensibility.

With composition we get code re-use, as we get with inheritance. Composition is more flexible and a means to designing loosely coupled applications. Here is a UML diagram showing our example project.

First, here is our logger code. It just has a method that writes to the console so we can see that it was executed.

    class Logger
    {
        public void Log(string message) {
            Console.WriteLine("Log it: " + message);
        }
    }

Here is the Installer. Notice that we have a constructior that takes a Logger.

    class Installer
    {
        private readonly Logger _logger;
        public Installer(Logger logger) {
            _logger = logger;
        }
        public void Install() {
            _logger.Log("installing...");
        }
    }

Here is the DbMigrator.

    class DbMigrator
    {
        private readonly Logger _logger;
        public DbMigrator(Logger logger) {
            _logger = logger;
        }
        public void Migrate() {
            _logger.Log("we are migrating...");
        }
    }

Below is our program. Notice that the first three lines of code create a dbMigrator and an Installer. In the first of the three lines, we create a logger object and pass it all in one line of code. In the second line of code, we create a new Logger object called logger. In the third line, we pass the logger object.

namespace Composition
{
    class Program
    {   // Mosh Hamedani at Udemy.com
        static void Main(string[] args)
        {
            var dbMigrator = new DbMigrator(new Logger());
            var logger = new Logger();
            var installer = new Installer(logger);

            dbMigrator.Migrate();
            installer.Install();
        }
    }
}

A Private Field and a Constructor

In our two classes that use the logger, they each have a private field that contains the logger. That field is of the type of our class: Logger. To say it again, they have a private field that contains the custom class logger. Also, they each have a constructor that we wrote. The constructor requires a logger object that is used to initialize the private field. The Logger is a private field in the Installer and the DbMigrator.

The related class is a private filed in the composite class.

using System;
namespace Composition
{ 
    class Program
    {   // Mosh Hamedani at Udemy.com - code has been modified.
        static void Main(string[] args)
        {
            var logger = new Logger();

            logger.dateTime = DateTime.Now;  // current date and time
            var dbMigrator = new DbMigrator(logger);
            dbMigrator.Migrate();

            logger.dateTime = DateTime.Now;  // current date and time
            var installer = new Installer(logger);
            installer.Install("programA");

            logger.dateTime = DateTime.Now;  // current date and time
            installer.Location = "D:\\Program Files";  // to D: not C:
            installer.Install("programB");
        }
    }
}
Series Navigation<< C# Inheritance IntroductionC# Composition Part 2 >>