SOLID principles in .NET revisited part 10: concentrating on enumerations resolved

Introduction

In the previous post we built some bad code. We built it like that deliberately to make you aware of how easy it is to misuse enumerations. We ended up with classes that are coupled in a way that if you extend one then you’ll need to extend at least one other.

In this post we’ll refactor the code to make it better.

Objects instead of enumerations

The main point I want to make in this exercise is that building “proper” objects instead of enumerations can make your code base much more flexible. It’s important to note that there are multiple ways to improve the code. I’m only going to present a solution that I think is good. You might come up with a different solution and it may well be just as good or even superior. The main thing is that the components of your code base should be as loosely coupled as possible.

The PerformanceMetricType enumeration and its members will be transformed into an abstract base class and two implementations. The abstract class will have a single “description” parameter so that the Metric type can be described. It will also have an abstract method where the implementing type will have to extract the correct value from the PerformanceSummary object:

public abstract class Metric
{
	private string _description;

	public Metric(String description)
	{
		if (string.IsNullOrEmpty(description)) throw new ArgumentNullException("Metric description");
		_description = description;
	}

	public string Description
	{
		get
		{
			return _description;
		}
	}

	public abstract double ExtractActualValueFrom(PerformanceSummary performanceSummary);
}

Here come the implementations as well. First AverageResponseTimeMetric:

public class AverageResponseTimeMetric : Metric
{
	public AverageResponseTimeMetric() : base("Average response time per page")
	{}

	public override double ExtractActualValueFrom(PerformanceSummary performanceSummary)
	{
		return performanceSummary.AverageResponseTimePerPage;
	}
}

…and second the UrlCallsPassedPerMinuteMetric:

public class UrlCallsPassedPerMinuteMetric : Metric
{
	public UrlCallsPassedPerMinuteMetric() : base("Url calls passed per minute")
	{}

	public override double ExtractActualValueFrom(PerformanceSummary performanceSummary)
	{
		return performanceSummary.TotalPassedCallsPerMinute;
	}
}

We’ll do the same with the EvaluationOperator enumeration. The abstract base class EvaluationOperation also has a description field and a single abstract method. The implementing class will have to determine whether the actual value breaks a certain limit:

public abstract class EvaluationOperation
{		
	private string _description;

	public EvaluationOperation(String description)
	{
		if (string.IsNullOrEmpty(description)) throw new ArgumentNullException("Description");
		_description = description;
	}

	public string Description
	{
		get
		{
			return _description;
		}
	}

	public abstract bool LimitBroken(double limit, double actual);
}

Here’s the GreaterThan implementation of EvaluationOperation:

public class GreaterThan : EvaluationOperation
{
	public GreaterThan() : base ("Greater than")
	{}

	public override bool LimitBroken(double limit, double actual)
	{
		return actual > threshold;
	}
}

…and here comes LessThan:

public class LessThan : EvaluationOperation
{
	public LessThan() : base("Less than")
	{ }

	public override bool LimitBroken(double limit, double actual)
	{
		return actual < threshold;
	}
}

ThresholdEvaluationResult is the same as before:

public class ThresholdEvaluationResult
{
	public bool ThresholdBroken { get; set; }
}

The updated Threshold class looks quite different from what we had before. The enumeration parameters are gone from the constructor and have been replaced by abstract classes. Also, the logic of evaluating whether a threshold is broken has been placed within the Threshold class instead of some specialised “service”:

public class Threshold
{
	private Metric _metric;
	private EvaluationOperation _operation;
	private double _thresholdValue;

	public Threshold(Metric metric, EvaluationOperation operation, double thresholdValue)
	{
		if (metric == null) throw new ArgumentNullException("Metric");
		if (operation == null) throw new ArgumentNullException("Operation");
		_metric = metric;
		_operation = operation;
		_thresholdValue = thresholdValue;
	}

	public ThresholdEvaluationResult Evaluate(PerformanceSummary performanceSummary)
	{
		ThresholdEvaluationResult res = new ThresholdEvaluationResult();
		double actualValue = _metric.ExtractActualValueFrom(performanceSummary);
		bool broken = _operation.LimitBroken(_thresholdValue, actualValue);
		res.ThresholdBroken = broken;
		return res;
	}
}

As you see the Evaluate method calls upon the injected abstract classes in order to determine whether some limit has been broken. The Threshold class knows nothing about the actual implementations. Also, if you need to extend your domain with other Operation types, such as EqualTo, LessThanOrEqualTo or implement new metric types, like CpuUsageMetric, then you can do that in isolation. Other classes in the model won’t need to be updated at all. The Threshold class will happily accept the new implementations without any complaints.

This post concludes our discussion of SOLID in .NET for the time being.

View the list of posts on Architecture and Patterns here.

Advertisement

SOLID principles in .NET revisited part 9: concentrating on enumerations

Introduction

In the previous post we streamlined our demo code so that it adheres to the SOLID principles. You may still spot bits and pieces that violate some design principle. It’s important to remark that in a large enterprise project it’s very difficult to attain 100% SOLID, if that state exists at all. You might be able to spot “deviations” even in the most well-maintained code bases.

In this post we’ll concentrate on enumerations. We saw before in this series how false usage of enums can easily lead to maintainability problems. Enumerations seem to be very popular due to their simplicity and how easily they can be used to create a list of valid values in a certain category.

We’ll first build up a short case study with all types of mistakes with special emphasis on enumerations. We’ll then improve the code in the next post.

Read more of this post

SOLID principles in .NET revisited part 8: clean-up

Introduction

In the previous post we covered letter ‘D’, i.e. the Dependency Inversion Principle among the SOLID principles.
A class can have a number of dependencies in order to perform its functions properly. Applying DIP will open an entry point for the clients to supply their own implementations for those dependencies when they call upon that class. The class in turn can remove all responsibility of creating concrete implementations for its abstract dependencies. The result will be loosely coupled code where a class won’t be tightly coupled to concrete services.

In this post we’ll only clean up a couple of remaining issues in the code.

Remove “virtual”

In the post on OCP we mentioned that the “virtual” keyword provided an extensibility point for a class. However, since then we know that abstractions provide a much better alternative. Hence we don’t have to make our methods virtual any more:

Read more of this post

SOLID principles in .NET revisited part 7: the Dependency Inversion Principle

Introduction

In the previous post we saw the definition of the Interface Segregation Principle. We applied it to a problematic case where a class could not fully implement the IAuthorizationService interface. We then broke up the interface into two parts so that they became more specialised. A consequence of ISP is often a large number of small, very specialised interfaces of 1 or maybe 2 methods. Large, monolithic interfaces are to be avoided as it will be more difficult to find concrete classes that can meaningfully implement all interface methods.

We’ve reached the last letter in the SOLID acronym, i.e. ‘D’ which stands for the Dependency Inversion Principle.

Read more of this post

SOLID principles in .NET revisited part 6: the Interface Segregation Principle

Introduction

In the previous post we continued to discuss the Liskov Substitution Principle. We saw a subtle example of breaking LSP by trying to force an object to implement an interface even though not all methods of the interface made sense for it. The client object, i.e. OnDemandAgentService cannot consume all implementations of IAuthorizationService without fully being able to carry out its tasks.

A possible solution comes in the form of letter ‘I’ in SOLID, which stands for the Interface Segregation Principle (ISP). ISP states that clients should not be forced to depend on interfaces and methods they do not use. Applying ISP correctly will result in a lot of small interfaces instead of handful of large ones with lots of methods. The more methods to an interface has the more likely it is that an implementation will not be able to fulfil all parts of the contract. The IAuthorizationService only has 2 methods and we immediately found an example where a class, the AuthorizationService only could implement one of them.

Read more of this post

SOLID principles in .NET revisited part 5: the Liskov Substitution Principle 2

Introduction

In the previous post we looked at letter ‘L’ in SOLID, i.e. the Liskov Substitution Principle. We saw how adhering to the LSP helps remove implementation-specific details from a client class that consumes those implementations. The client class will be able to consume the services of an abstraction without worrying about the actual implementation of the abstraction. The goal of LSP is that any implementation of an abstraction will work seamlessly within the consuming class.

We looked at the following cases that can indicate a violation of LSP:

  • switch or if-else blocks checking for the value of an enumeration
  • Code blocks that check the actual type of an abstraction to branch logic. This can be coupled with downcasting the abstraction to a concrete implementation type
  • Blocks with magic strings that check for a value in some string parameter and branch logic accordingly

There’s at least one more indicator which we haven’t seen so far. I decided to devote a separate article to that case as it brings us closer to the next constituent of SOLID, i.e. the Interface Segregation Principle.

Read more of this post

SOLID principles in .NET revisited part 4: the Liskov Substitution Principle

Introduction

In the previous post we looked at the Open-Closed Principle, i.e. ‘O’ in the SOLID acronym. We saw how the OCP facilitated the flexibility and extensibility of a class. It also places a constraint on a class by making it “append-only”. In other words you can extend a class but you cannot change the implementation of existing parts of a class.

In this post we’ll take a look at letter ‘L’ which stands for the Liskov Substitution Principle LSP.

Definition of LSP

LSP stands for the interchangeability of different implementations of an abstraction. An abstraction in C#, and probably most other popular object-oriented programming languages, can either be an abstract base class or an interface. According to LSP it shouldn’t make any difference what implementation of an abstraction you call from a client. Any concrete implementation of the abstraction should behave in a way that doesn’t break the client and doesn’t produce any unexpected and/or incorrect results. The client shouldn’t ever be concerned with the implementation details of an abstraction. It should be able to consume any concrete implementation “without batting an eye”.

Read more of this post

SOLID principles in .NET revisited part 3: the Open-Closed principle

Introduction

In the previous post we looked at the letter ‘S’ in SOLID, i.e. the single responsibility principle. We identified the reasons for change in the OnDemandAgentService class and broke out 3 of them into separate classes. The OnDemandAgentService can now concentrate on starting a new on-demand agent and delegate other tasks to specialised services such as EmailService. Also, an additional benefit is that other classes in the assembly can re-use those specialised services so that e.g. logging can be centralised.

In this post we’ll look at the second constituent of the SOLID principles, the letter ‘O’ which stands for the Open-Closed Principle OCP.

Read more of this post

SOLID principles in .NET revisited part 2: Single Responsibility Principle

Introduction

In the previous post we set the tone for the recycled series on SOLID. We also presented a piece of code to be improved. There are several issues with this code and we’ll try to improve it step by step as we go through the principles behind SOLID.

In this post we’ll start with ‘S’, i.e. the Single Responsibility Principle (SRP). We’ll partly improve the code by applying SRP to it. Note that the code will still have a lot of faults left after this but we’ll improve it gradually.

Read more of this post

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”.

Read more of this post

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: