Rewriting Hello World according to SOLID in .NET

Introduction

There are numerous posts on this blog dedicated to various software principles. One of those principles is SOLID with special attention to ‘D’, i.e. the dependency inversion principle (DIP).

In this short post we’ll rewrite the classic introductory C# Hello World program according to what we’ve learnt before.

Demo

The starting point of the exercise is the good old one-liner Hello World programme:

static void Main(string[] args)
{
	Console.WriteLine("Hello world");
}

We can immediately see a couple of flaws with this solution if we view it from a SOLID point of view:

  • We can only write to the Console – if we want to write to a file then we’ll have to modify Main
  • We can only print Hello world to the console – we have to manually overwrite this bit of code if we want to print something else
  • We cannot easily extend this application in a sense that it lacks any seams that we discussed before – what if we want to add logging or security checks?

Let’s try to rectify these shortcomings. We’ll tackle the problem of message printing first. The Adapter pattern solves the issue by abstracting away the Print operation in an interface:

public interface ITextWriter
{
	void WriteText(string text);
}

We can then implement the Console-based solution as follows:

public class ConsoleTextWriter : ITextWriter
{
	public void WriteText(string text)
	{
		Console.WriteLine(text);
	}
}

Next let’s find a solution for collecting what the text writer needs to output. We’ll take the same approach and follow the adapter pattern:

public interface IMessageCollector
{
	string CollectMessageFromUser();
}

…with the corresponding Console-based implementation looking like this:

public class ConsoleMessageCollector : IMessageCollector
{
	public string CollectMessageFromUser()
	{
		Console.Write("Type your message to the world: ");
		return Console.ReadLine();
	}
}

These loose dependencies must be injected into another object, let’s call it PublicMessage:

public class PublicMessage
{
	private readonly IMessageCollector _messageCollector;
	private readonly ITextWriter _textWriter;

	public PublicMessage(IMessageCollector messageCollector, ITextWriter textWriter)
	{
		if (messageCollector == null) throw new ArgumentNullException("Message collector");
		if (textWriter == null) throw new ArgumentNullException("Text writer");
		_messageCollector = messageCollector;
		_textWriter = textWriter;
	}

	public void Shout()
	{
		string message = _messageCollector.CollectMessageFromUser();
		_textWriter.WriteText(message);
	}
}

You’ll realise some of the most basic techniques we’ve looked at in this series: constructor injection, guard clause, readonly private backing fields.

We can use these objects from Main as follows:

static void Main(string[] args)
{
	IMessageCollector messageCollector = new ConsoleMessageCollector();
	ITextWriter textWriter = new ConsoleTextWriter();
	PublicMessage publicMessage = new PublicMessage(messageCollector, textWriter);
	publicMessage.Shout();

	Console.ReadKey();
}

Now we’re free to inject any implementation of those interfaces: read from a database and print to file; read from a file and print to an email; read from the console and print to some web service. The PublicMessage class won’t care, it’s oblivious of the concrete implementations.

This solution is a lot more extensible. We can use the decorator pattern to add functionality to the text writer. Let’s say we want to add logging to the text writer through the following interface:

public interface ILogger
{
	void Log();
}

We can have some default implementation:

public class DefaultLogger : ILogger
{
	public void Log()
	{
		//implementation ignored
	}
}

We can wrap the text printing functionality within logging as follows:

public class LogWriter : ITextWriter
{
	private readonly ILogger _logger;
	private readonly ITextWriter _textWriter;

	public LogWriter(ILogger logger, ITextWriter textWriter)
	{
		if (logger == null) throw new ArgumentNullException("Logger");
		if (textWriter == null) throw new ArgumentNullException("TextWriter");
		_logger = logger;
		_textWriter = textWriter;
	}

	public void WriteText(string text)
	{
		_logger.Log();
		_textWriter.WriteText(text);
	}
}

In Main you can have the following:

static void Main(string[] args)
{
	IMessageCollector messageCollector = new ConsoleMessageCollector();
	ITextWriter textWriter = new LogWriter(new DefaultLogger(), new ConsoleTextWriter());
	PublicMessage publicMessage = new PublicMessage(messageCollector, textWriter);
	publicMessage.Shout();

	Console.ReadKey();
}

Notice that we didn’t have to do anything to PublicMessage. We passed in the interface dependencies as before and now we have the logging function included in message writing. Also, note that Main is tightly coupled to a range of objects, but it is acceptable in this case. We construct our objects in the entry point of the application, i.e. the composition root which is the correct place to do that. We don’t new up any dependencies within PublicMessage.

This was of course a very contrived example. We expanded the original code to a lot more complex solution with a lot higher overhead. However, real life applications, especially enterprise ones are infinitely more complicated where requirements change a lot. Customers are usually not sure what they want and wish to include new and updated features in the middle of the project. It’s vital for you as a programmer to be able to react quickly. Enabling loose coupling like that will make your life easier by not having to change several seemingly unrelated parts of your code.

View the list of posts on Architecture and Patterns here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

5 Responses to Rewriting Hello World according to SOLID in .NET

  1. cyrilogc says:

    Hello Andras,

    That’s a great great post again! Thank you! 🙂
    I was wondering if it could be a good thing (or not) to let LogWriter class being responsible of writting logs, by implementing directly ILogger.
    In order to avoid passing ILogger as parameter and to have a real decorator with just TextWriter as decorated class passing as parameter.
    What do you think about it?

    Merry Christmas by the way!! 🙂

  2. Kevin says:

    You took a subject that has eluded me in the past with a very simple and concise example. Thank you Andras. I’m definitely going to look at the other stuff on your site.

  3. Pingback: SOLID exercises - Karol Bocian

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: