Design for change

When designing for change, you should change the what to the how.

The what is the requirement of the business. As any seasoned person involved in a role within software development will tell you that requirements frequently change. As such, the software has to be adaptable to meet those changes. The business is not interested in how the requirements are implemented by the software and infrastructure teams, only that the requirements are met precisely on time and on budget.

On the other hand, the software and infrastructure teams are more focused on how those business requirements are to be met. Regardless of the technology and processes that are adopted for the project to implement the requirements, the software and target environment must be adaptable to changing requirements.

But that is not all. You see, software versions often change with bug fixes and new features. As new features are implemented and refactoring takes place, the software code becomes deprecated and eventually obsolete. On top of that, software vendors have a road map of their software that forms part of their application life cycle management. Eventually, software versions get to the point where they are retired and no longer supported by the vendor. This can force a major migration from the current version, which will no longer be supported, to the new supported version, and this can bring with it breaking changes that must be addressed.

Interface-oriented programming

Interface-Oriented Programming (IOP) helps us to program polymorphic code. Polymorphism in OOP is defined as different classes having their own implementations of the same interface. And so, by using interfaces, we can morph our software to meet the needs of the business.

Let's consider a database connection example. An application may be required to connect to different data sources. But how can the database code remain the same no matter what database is employed? Well, the answer lies in the use of interfaces.

You have different database connection classes that implement the same database connection interface, but they each have their own versions of the implemented methods. This is known as polymorphism. The database then accepts a database connection parameter that is of the database connection interface type. You can then pass into the database any database connection type that implements the database connection interface. Let's code this example so that it makes things a little more clear.

Start by creating a simple .NET Framework console application. Then update the Program class as follows:

static void Main(string[] args)
{
var program = new Program();
program.InterfaceOrientedProgrammingExample();
}

private void InterfaceOrientedProgrammingExample()
{
var mongoDb = new MongoDbConnection();
var sqlServer = new SqlServerConnection();
var db = new Database(mongoDb);
db.OpenConnection();
db.CloseConnection();
db = new Database(sqlServer);
db.OpenConnection();
db.CloseConnection();
}

In this code, the Main() method creates a new instance of the Program class and then calls the InterfaceOrientedProgrammingExample() method. In that method, we instantiate two different database connections, one for MongoDB and one for SQL Server. We then instantiate the database with a MongoDB connection, open the database connection, and then close it. Then we instantiate a new database using the same variable and pass in a SQL Server connection, then open the connection and close the connection. As you can see, we only have one Database class with a single constructor, yet the Database class will work with any database connection that implements the required interface. So, let's add the IConnection interface:

public interface IConnection
{
void Open();
void Close();
}

The interface has only two methods called Open() and Close(). Add the MongoDB class that will implement this interface:

public class MongoDbConnection : IConnection
{
public void Close()
{
Console.WriteLine("Closed MongoDB connection.");
}

public void Open()
{
Console.WriteLine("Opened MongoDB connection.");
}
}

We can see that the class implements the IConnection interface. Each method prints out a message to the console. Now add that SQLServerConnection class:

public class SqlServerConnection : IConnection
{
public void Close()
{
Console.WriteLine("Closed SQL Server Connection.");
}

public void Open()
{
Console.WriteLine("Opened SQL Server Connection.");
}
}

The same goes for the Database class. It implements the IConnection interface, and for each method invocation, a message is printed to the console. And now for the Database class, as follows:

public class Database
{
private readonly IConnection _connection;

public Database(IConnection connection)
{
_connection = connection;
}

public void OpenConnection()
{
_connection.Open();
}

public void CloseConnection()
{
_connection.Close();
}
}

The Database class accepts an IConnection parameter. This sets the _connection member variable. The OpenConnection() method opens the database connection, and the CloseConnection() method closes the database connection. Well, it's time to run the program. You should see the following output in the console window:

Opened MongoDB connection.
Closed MongoDB connection.
Opened SQL Server Connection.
Closed SQL Server Connection.

So now, you can see the advantage of programming to interfaces. You can see how they enable us to extend the program without having to modify the existing code. That means that if we need to support more databases, then all we have to do is write more connection objects that implement the IConnection interface.

Now that you know how interfaces work, we can look at how to apply them to dependency injection and inversion of control. Dependency injection helps us to write clean code that is loosely coupled and easy to test, and inversion of control enables the interchanging of software implementations as required, as long as those implementations implement the same interface.

Dependency injection and inversion of control

In C#, we have the ability to address changing software needs using Dependency Injection (DI) andInversion of Control (IoC). These two terms do have different meanings but are often used interchangeably to mean the same thing.

With IoC, you program a framework that accomplishes tasks by calling modules. An IoC container is used to keep a register of modules. These modules are loaded when requested by the user or configuration requests them.

DI removes internal dependencies from classes. Dependent objects are then injected by an external caller. An IoC container uses DI to inject dependent objects into an object or method.

In this chapter, you will find some useful resources that will help you to understand IoC and DI. You will then be able to use these techniques in your programs.

Let's see how we can implement our own simple DI and IoC without any third-party frameworks.

An example of DI

In this example, we are going to roll our own simple DI. We will have an ILogger interface that will have a single method with a string parameter. We will then produce a class called TextFileLogger that implements the ILogger interface and outputs a string to a text file. Finally, we will have a Worker class that will demonstrate constructor injection and method injection. Let's look at the code.

The following interface has a single method that will be used for implementing classes to output a message according to the implementation of the method:

namespace CH3.DependencyInjection
{
public interface ILogger
{
void OutputMessage(string message);
}
}

The TexFileLogger class implements the ILogger interface and outputs the message to a text file:

using System;

namespace CH3.DependencyInjection
{
public class TextFileLogger : ILogger
{
public void OutputMessage(string message)
{
System.IO.File.WriteAllText(FileName(), message);
}

private string FileName()
{
var timestamp = DateTime.Now.ToFileTimeUtc().ToString();
var path = Environment.GetFolderPath(Environment
.SpecialFolder.MyDocuments);
return $"{path}_{timestamp}";
}
}
}

The Worker class provides an example of constructor DI and method DI. Notice that the parameter is an interface. So, any class that implements that interface can be injected at runtime:

namespace CH3.DependencyInjection
{
public class Worker
{
private ILogger _logger;

public Worker(ILogger logger)
{
_logger = logger;
_logger.OutputMessage("This constructor has been injected
with a logger!");
}

public void DoSomeWork(ILogger logger)
{
logger.OutputMessage("This methods has been injected
with a logger!");
}
}
}

The DependencyInject method runs the example to show DI in action:

        private void DependencyInject()
{
var logger = new TextFileLogger();
var di = new Worker(logger);
di.DoSomeWork(logger);
}

As you can see with the code we've just looked at, we start by producing a new instance of the TextFileLogger class. This object is then injected into the constructor of the worker. We then call the DoSomeWorkmethod and pass in the TextFileLogger instance. In this simple example, we have seen how to inject code into a class via its constructor and via methods.

What is good about this code is it removes the dependency between the worker and the TextFileLogger instance. This makes it easy for us to replace the TextFileLogger instance with any other type of logger that implements the ILogger interface. So we could have used, for example, an event viewer logger or even a database logger. Using DI is a good way to reduce coupling in your code.

Now that we've seen DI at work, we should also look at IoC. And we'll do that now.

An example of IoC

In this example, we are going to register dependencies with an IoC container. We will then use DI to inject the necessary dependencies.

In the following code, we have an IoC container. The container registers the dependencies to be injected in a dictionary, and reads values from the configuration metadata:

using System;
using System.Collections.Generic;

namespace CH3.InversionOfControl
{
public class Container
{
public delegate object Creator(Container container);

private readonly Dictionary<string, object> configuration = new
Dictionary<string, object>();
private readonly Dictionary<Type, Creator> typeToCreator = new
Dictionary<Type, Creator>();

public Dictionary<string, object> Configuration
{
get { return configuration; }
}

public void Register<T>(Creator creator)
{
typeToCreator.Add(typeof(T), creator);
}

public T Create<T>()
{
return (T)typeToCreator[typeof(T)](this);
}

public T GetConfiguration<T>(string name)
{
return (T)configuration[name];
}
}
}

Then, we create a container, and we use the container to configure metadata, register types, and create instances of dependencies:

private void InversionOfControl()
{
Container container = new Container();
container.Configuration["message"] = "Hello World!";
container.Register<ILogger>(delegate
{
return new TextFileLogger();
});
container.Register<Worker>(delegate
{
return new Worker(container.Create<ILogger>());
});
}

Next up, we will look at how to limit an object's knowledge to knowing only about its close relatives using the Law of Demeter. This will help us to write a clean C# code that avoids the use of navigation trains.