Code Reuse via Composition in C#

Composition is a common object oriented concept that enables us to implement loose coupling as well as code-reuse. Lot of times we recommend our developers to use more composition and less inheritance due this very fact. Inheritance supports code-reuse, but makes application tightly coupled and creates long inheritance hierarchies that creates inter-dependencies. Inheritance implements a IS-A relationship where composition implements a HAS-A relationship – kind of a House has-a door. Below is a working example to demonstrate how composition is implemented in C# and how it leads to code reuse and loosely coupled architecture. Logger is a common class that is being reused between two different scenarios and without the need to inherit anything from it that eliminates any dependency between the classes.

Class that is re-used:

using System;
 
namespace Composition
{
    public class Logger
    {
        public void LogCommand(string logcommand)
        {
            Console.WriteLine(logcommand);
        }
    }
}

Class 1 that re-uses by Composition: 

namespace Composition
{
    public class RoboInstall
    {
        private readonly Logger _logger;
 
        public RoboInstall(Logger logger)
        {
            _logger = logger;
        }
 
        public void InstallRobo()
        {
            _logger.LogCommand("We are Installing our new Robot!!!");
        }
    }
}

Class 2 that re-uses by Composition: 

namespace Composition
{
    public class RoboMigrator
    {
        private readonly Logger _logger;
 
        public RoboMigrator(Logger logger)
        {
            _logger = logger;
        }
 
        public void MigrateRobo()
        {
            _logger.LogCommand("We are migrating Robot!!");
        }
    }
}

Note that we use the private read only field to define the re-used object which is instantiated and created via the constructor.

Main program:

namespace Composition
{
    class Program
    {
        static void Main(string[] args)
        {
            var migrate = new RoboMigrator(new Logger());
 
            var install = new RoboInstall(new Logger());
 
            migrate.MigrateRobo();
 
            install.InstallRobo();
        }
    }
}

 As you can see in the main program, both RoboInstall and RoboMigrator injects the Logger object which is then available within migrate and install objects to the reused. Good thing to note is neither migrate nor install classes need to be recompiled if Logger class is changed and any changes will be instantaneously effected in both classes at the same time.