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.

1class Logger
2{
3    public void Log(string message) {
4        Console.WriteLine("Log it: " + message);
5    }
6}

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

1class Installer
2{
3    private readonly Logger _logger;
4    public Installer(Logger logger) {
5        _logger = logger;
6    }
7    public void Install() {
8        _logger.Log("installing...");
9    }
10}

Here is the DbMigrator.

1class DbMigrator
2{
3    private readonly Logger _logger;
4    public DbMigrator(Logger logger) {
5        _logger = logger;
6    }
7    public void Migrate() {
8        _logger.Log("we are migrating...");
9    }
10}

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.

1namespace Composition
2{
3    class Program
4    {   // Mosh Hamedani at Udemy.com
5        static void Main(string[] args)
6        {
7            var dbMigrator = new DbMigrator(new Logger());
8            var logger = new Logger();
9            var installer = new Installer(logger);
10 
11            dbMigrator.Migrate();
12            installer.Install();
13        }
14    }
15}

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.

1using System;
2namespace Composition
3{
4    class Program
5    {   // Mosh Hamedani at Udemy.com - code has been modified.
6        static void Main(string[] args)
7        {
8            var logger = new Logger();
9 
10            logger.dateTime = DateTime.Now;  // current date and time
11            var dbMigrator = new DbMigrator(logger);
12            dbMigrator.Migrate();
13 
14            logger.dateTime = DateTime.Now;  // current date and time
15            var installer = new Installer(logger);
16            installer.Install("programA");
17 
18            logger.dateTime = DateTime.Now;  // current date and time
19            installer.Location = "D:\\Program Files"// to D: not C:
20            installer.Install("programB");
21        }
22    }
23}
Series Navigation<< C# Inheritance IntroductionC# Composition Part 2 >>