SOLID principles in .NET revisited part 1: introduction with code to be improved

Introduction

Probably every single programmer out there wants to write good code. Nobody has the desire to be ashamed of the code base they have written. Probably no programmer wants to turn a large software project into a failure by deliberately writing low quality code.

What is good code anyway? Opinions differ on this point but we can generally say that good code means code that is straightforward to extend and maintain, code that’s easy to test, code that is flexible, code that is relatively easy to read, code that is difficult to break and code that can swiftly be adapted to changes in the requirements without weeks of refactoring. These traits are interdependent. E.g. code that’s flexible will be easier to change in line with new requirements. The English word “solid” has the meaning of “difficult to break” or “resistant to change” which is naturally applicable to good code.

However, it’s very difficult to write good code in practice. On the other hand it’s very easy to write bad code. Compilers do not understand software engineering principles so they won’t complain if your code is “bad” in any way – except if your code contains faulty syntax but that’s not what we mean by bad code here. Modern object oriented languages like C#, Java or Python provide a lot of flexibility to the programmer. He or she can construct code which performs one or more functions in lots of different ways. Also, different programmers might point out different parts in the same code base as being “bad”.

The SOLID design principles

How can we achieve good code then? There’s a set of generally accepted software design principles that can help you with that. These principles – there are 5 of them – make up an acronym which conveniently constitute the English word mentioned in the title: SOLID. Each letter in this acronym stands for a principle:

  • Single responsibility principle
  • Open-closed principle
  • Liskov substitution principle
  • Interface segregation principle
  • Dependency inversion principle

Probably every programmer with some enterprise level coding experience has heard of SOLID. However, most real life code probably doesn’t follow any of it. It happens to everyone because, as mentioned above, it’s very easy to write bad code. I can confess to writing code that kind of works but I am really ashamed of today. I have committed a lot of crimes against SOLID and other object-oriented design principles. Also, in practice it’s not always straightforward how to apply SOLID to a piece of code. Where do we start? How do we break up this code? Will we break anything else in the code base? Do we have the time? It’s really easy to give up these principles to produce code quickly that just works and can be deployed on our production servers to make management happy. They probably don’t give a damn about SOLID.

Another reason why programmers will omit SOLID is that it is often not part of the curriculum of software engineering. It may be introduced in a later stage because it is considered “advanced”. Most often programmers get to know SOLID only by chance: they hear about it in a conference, from a colleague or they read about it in some random blog post. However, by then they have already got into the habit of writing code quickly that works at that moment regardless of any design principles.

We discussed SOLID in this blog before. You can find the dedicated series starting here. However, it’s time to reinforce what we learnt using some new examples.

The code to be improved

I was considering the classic Customer-Product-Order triangle for the example but I finally decided to go with something from my work instead to make the exercise slightly more varied. We work, among other things, with performance testing of web sites. In order to simulate a certain amount of virtual users we need specially designated machines that in unison carry out a certain load test program against the target website. These special servers can be ones that are “on duty” all the time, i.e. are always listening for new load test jobs. Alternatively, if there are not enough dedicated load test servers or are not in the desired geographical location a new server can be constructed before the test and torn down when the test is complete. These are called on-demand servers as they are only used when needed. Cloud-based services, such as Amazon, MS Azure or Rackspace offer API service endpoints to start up and shut down servers. We’ll simulate a service that encapsulates logic to start such a server. We won’t of course implement any such service in this series as it is complex and has not much to do with the topic itself. We’ll mostly only work with skeleton code but you can imagine that the real implementation is a lot more complex.

The operation will be logged and authorized. Also, it is required that a sysadmin is notified if an on-demand server is started.

The following object represents an on-demand server or also called an on-demand agent:

public class OnDemandAgent
{
	public string Host { get; set; }
	public string Ip { get; set; }
	public string ImageId { get; set; }
}

Here’s the initial implementation of the OnDemandAgentService class:

public class OnDemandAgentService
{
	public OnDemandAgent StartNewOnDemandMachine()
	{
		LogInfo("Starting on-demand agent startup logic");
		try
		{
			if (IsAuthorized(Username, Password))
			{
				LogInfo(string.Format("User {0} will attempt to start a new on-demand agent.", Username));
				OnDemandAgent agent = StartNewAmazonServer();
				SendEmailToAdmin(string.Format("User {0} has successfully started a machine with ip {1}.", Username, agent.Ip));
				return agent;
			}
			else
			{
				LogWarning(string.Format("User {0} attempted to start a new on-demand agent."));
				throw new UnauthorizedAccessException("Unauthorized access to StartNewOnDemandMachine method.");
			}
		}
		catch (Exception ex)
		{
			LogError("Exception in on-demand agent creation logic");
			throw;
		}
	}

	public string Username { get; set; }
	public string Password { get; set; }

	private OnDemandAgent StartNewAmazonServer()
	{
		//Call Amazon API and start a new EC2 instance, implementation omitted
		OnDemandAgent amazonAgent = new OnDemandAgent();
		amazonAgent.Host = "usweav-ec2.mycompany.local";
		amazonAgent.Ip = "54.653.234.23";
		amazonAgent.ImageId = "ami-784930";
		return amazonAgent;
	}

	private void LogInfo(string info)
	{
		File.AppendAllText(@"c:\log\log.txt", string.Concat("INFO: ", info));
	}

	private void LogWarning(string warning)
	{
		File.AppendAllText(@"c:\log\log.txt", string.Concat("WARNING: ", warning));
	}

	private void LogError(string error)
	{
		File.AppendAllText(@"c:\log\log.txt", string.Concat("ERROR: ", error));
	}

	private bool IsAuthorized(string username, string password)
	{
		return (username == "admin" && password == "passw0rd");
	}

	private void SendEmailToAdmin(string message)
	{
		string emailHost = "email.mycompany.com";
		string recipient = "admin@mycompany.com";
		//actual email sending implementation omitted
	}
}

Look through the code, it should be very easy to follow. We only log to a file called log.txt on the File system. We have omitted the actual implementation of the server start-up, the email sending and user authorization logic. They do not matter to us, they are only an implementation detail. We don’t show that to the outside world so the principle of information hiding is more or less adhered to. The above code violates SOLID multiple times. However, it’s far from unthinkable that code with this low quality is in use in real life projects.

We’ll start improving it step by step in the next post.

View the list of posts on Architecture and Patterns here.

Advertisements

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

One Response to SOLID principles in .NET revisited part 1: introduction with code to be improved

  1. Pingback: Architecture and patterns | Michael's Excerpts

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

ultimatemindsettoday

A great WordPress.com site

iReadable { }

.NET Tips & Tricks

Robin Sedlaczek's Blog

Developer on Microsoft Technologies

HarsH ReaLiTy

A Good Blog is Hard to Find

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

the software architecture

thoughts, ideas, diagrams,enterprise code, design pattern , solution designs

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

Anything around ASP.NET MVC,WEB API, WCF, Entity Framework & AngularJS

Cyber Matters

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

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: