C# Composition Part 2


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

This post expands on the previous post C# Composition. We are going to expand our program from the last post.

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.

In this example, the logger is inside the DbMigrator and the Installer. We need to create a private variable for the Logger and place it inside the classes of the DbMigrator and Installer. We need a constructor for each of these classes that takes in a Logger object. In our Main() program we need to instantiate a Logger class to create a real object before we instantiate the Migrator and the Installer because we need to pass that Logger object to the constructor of DbMigrator and Installer.

Our Logger may have a few properties of its own. We could set these properties right after we instantiate the Logger. As an example of this, this program gets the current system date and time and sets the logger DateTime property.

The important thing of all of this is code reuse and encapsulation. The DbMigrator and the Installer only have to know what message they want to pass to the Logger. They don’t have to know about the details of how to write to a text file or to a database table. That’s for the logger to deal with. Any changes to the logger need only be done in one place. For example, if we wanted to add the ability to send an email message from the logger, we could just add that capability to the logger with new propertes and methods without having to change any code in the DbMigrator or the Installer. We could add a method to the Logger called SendEmail. The DbMigrator or Installer just has to call that method.

using System;
namespace Composition
{ 
    class Program
    {   // Mosh Hamedani at Udemy.com - code 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");
        }
    }
}

Logger.cs. The logger here is very simple here, for teaching purposes.

using System;
namespace Composition
{
    class Logger
    {
        public DateTime dateTime { get; set; }
        public void Log(string message)
        {   // very simple here for teaching purposes but
            // we could be opening and appending to a file
            // or even a database table.
            Console.WriteLine("Logger message: " + message);
        }
    }
}

DbMigrator.cs.

namespace Composition
{
    class DbMigrator
    {
        private readonly Logger _logger;
        public DbMigrator(Logger logger)
        {
            _logger = logger;
        }
        public void Migrate()
        {
            _logger.Log("we are migrating... " + _logger.dateTime);
            Thread.Sleep(1500); // delay 1.5 second
            _logger.Log("");
        }
    }
}

Installer.cs

using System.Threading;
namespace Composition
{
    class Installer
    {
        private readonly Logger _logger;
        public string Location { get; set; } = "C:\\Program Files";  // initialize to default
        public Installer(Logger logger)  // constructor
        {
            _logger = logger;  
        }
        public void Install(string programName)
        {
            _logger.Log("start installing " + programName + " to " + Location);
            _logger.Log(_logger.dateTime.ToLongDateString() + " " + _logger.dateTime.ToLongTimeString());
            _logger.Log("checking for disc space and so on and so on...");
            Thread.Sleep(1500); // delay 1.5 second
            _logger.Log("done " + programName + " to " + Location);
            _logger.Log("");
        }
    }
}

Here is the output.

Series Navigation<< C# CompositionC# Constructors and Inheritance >>