Refactoring string into the specific type
Tags: csharp, ood
Published: 2020-05-22 08:27:21

---
title: "Refactoring string into the specific type"
published: true
description: An example when string is too broad type to capture all domain requirements and how to handle it 
tags: ["Csharp", "OOD"]
cover_image: https://img.buzzfeed.com/buzzfeed-static/static/2015-06/8/0/enhanced/webdr08/original-18310-1433738125-3.jpg
canonical_url: https://wkalmar.github.io/post/refactoring-string-to-specific-types/
---
## Introduction
While the article title may sound controversial as there is clearly nothing wrong with using `string` in your code below I'll show the case where `string` type doesn't clearly communicate all the necessary properties of a domain in question. Then I'll show how this can be handled. You can watch full code on [Github](https://github.com/Wkalmar/Orckestra).

## The code
Recently I was tasked to write the code which converts Linux permissions to their octal representation.
Nothing too fancy, just a static class that does the job. Here's the code
```csharp
internal class PermissionInfo
{
	public int Value { get; set; }
	public char Symbol { get; set; }
}

public static class SymbolicUtils
{
	private const int BlockCount = 3;
	private readonly static Dictionary<int, PermissionInfo> Permissions = new Dictionary<int, PermissionInfo>() {
			{0, new PermissionInfo {
				Symbol = 'r',
				Value = 4
			} },
			{1, new PermissionInfo {
				Symbol = 'w',
				Value = 2
			}},
			{2, new PermissionInfo {
				Symbol = 'x',
				Value = 1
			}} };



	public static int SymbolicToOctal(string input)
	{
		if (input.Length != 9)
		{
			throw new ArgumentException("input should be a string 3 blocks of 3 characters each");
		}
		var res = 0;
		for (var i = 0; i < BlockCount; i++)
		{
			res += ConvertBlockToOctal(input, i);
		}
		return res;
	}

	private static int ConvertBlockToOctal(string input, int blockNumber)
	{
		var res = 0;
		foreach (var (index, permission) in Permissions)
		{
			var actualValue = input[blockNumber * BlockCount + index];
			if (actualValue == permission.Symbol)
			{
				res += permission.Value;
			}
		}
		return res * (int)Math.Pow(10, BlockCount - blockNumber - 1);
	}
}
```
The code does it's job. However, it left me unsatisfied because the knowledge about the permission is scattered all over the place i.e. magic number 9 or `ConvertBlockToOctal` method which relies on permissions being in a certain well-defined order. This left me wondering whether the string is the best way to represent Linux permission.

The question is rather rhetorical since Linux permission possesses additional constraints which string as a general datatype doesn't. So the idea is to impose those restrictions on my input datatype or using OOD terminology encapsulate them.

## Testing
Automated tests are the necessary prerequisite for each refactoring. For this task, my test-suite isn't really exhaustive, but it is enough to cover the case provided in the spec.
```csharp
[Fact]
public void HandlesCorrectInput()
{
	SymbolicUtils.SymbolicToOctal("rwxr-x-w-").Should().Be(752);
}
```

## Extracting SymbolicPermission class
```csharp
internal class SymbolicPermission
{
	private struct PermissionInfo
	{
		public int Value { get; set; }
		public char Symbol { get; set; }
	}

	private const int BlockCount = 3;
	private const int BlockLength = 3;
	private const int MissingPermissionSymbol = '-';

	private readonly static Dictionary<int, PermissionInfo> Permissions = new Dictionary<int, PermissionInfo>() {
			{0, new PermissionInfo {
				Symbol = 'r',
				Value = 4
			} },
			{1, new PermissionInfo {
				Symbol = 'w',
				Value = 2
			}},
			{2, new PermissionInfo {
				Symbol = 'x',
				Value = 1
			}} };

	private string _value;

	private SymbolicPermission(string value)
	{
		_value = value;
	}

	public static SymbolicPermission Parse(string input)
	{
		if (input.Length != BlockCount * BlockLength)
		{
			throw new ArgumentException("input should be a string 3 blocks of 3 characters each");
		}
		for (var i = 0; i < input.Length; i++)
		{
			TestCharForValidity(input, i);
		}

		return new SymbolicPermission(input);
	}

	public int GetOctalRepresentation()
	{
		var res = 0;
		for (var i = 0; i < BlockCount; i++)
		{
			res += ConvertBlockToOctal(_value, i);
		}
		return res;
	}

	private static void TestCharForValidity(string input, int position)
	{
		var index = position % BlockLength;
		var expectedPermission = Permissions[index];
		var symbolToTest = input[position];
		if (symbolToTest != expectedPermission.Symbol && symbolToTest != MissingPermissionSymbol)
		{
			throw new ArgumentException($"invalid input in position {position}");
		}
	}

	private static int ConvertBlockToOctal(string input, int blockNumber)
	{
		var res = 0;
		foreach (var (index, permission) in Permissions)
		{
			var actualValue = input[blockNumber * BlockCount + index];
			if (actualValue == permission.Symbol)
			{
				res += permission.Value;
			}
		}
		return res * (int)Math.Pow(10, BlockCount - blockNumber - 1);
	}
}
```
What happened is that class `SymbolicPermission` now holds all the knowledge about Linux permission structure. The heart of this code is the `Parse` method that checks whether the string input matches all the necessary requirements. Another point to highlight is the use of a private constructor.
```csharp
private SymbolicPermission(string value)
{
	_value = value;
}
```
This makes `Parse` the single entry point thus disabling the possibility to create permission that doesn't match all the required constraints.

Now the usage of the old static method looks as simple as
```csharp
public static int SymbolicToOctal(string input)
{
	var permission = SymbolicPermission.Parse(input);
	return permission.GetOctalRepresentation();
}
```

## Bonus: refactoring SRP violation
At this point, `ConvertBlockToOctal` method not only converts a block of permissions to its octal representation but also extracts it from the provided input.
```csharp
private static int ConvertBlockToOctal(string input, int blockNumber)
{
	var res = 0;
	foreach (var (index, permission) in Permissions)
	{
		var actualValue = input[blockNumber * BlockCount + index];
		if (actualValue == permission.Symbol)
		{
			res += permission.Value;
		}
	}
	return res * (int)Math.Pow(10, BlockCount - blockNumber - 1);
}
```
This violates [single responsibility principle](https://en.wikipedia.org/wiki/Single_responsibility_principle). This is the reason why we'll split this code into two methods.
```csharp
private string GetBlock(int blockNumber)
{
	return _value.Substring(blockNumber * BlockLength, BlockLength);
}

private int ConvertBlockToOctal(string block)
{
	var res = 0;
	foreach (var (index, permission) in Permissions)
	{
		var actualValue = block[index];
		if (actualValue == permission.Symbol)
		{
			res += permission.Value;
		}
	}
	return res;
}
```
Let's have a look at how they are called
```csharp
public int GetOctalRepresentation()
{
	var res = 0;
	for (var i = 0; i < BlockCount; i++)
	{
		var block = GetBlock(i);
		res += ConvertBlockToOctal(block) * (int)Math.Pow(10, BlockCount - i - 1);
	}
	return res;
}
```

## Conclusion
Often string is a jack-of-all-trades type that does not represent all the necessary constraints that actual type in question possesses. As one of the possible solutions in this article, I propose crafting dedicated types and using `Parse` method in order to construct specific type from the general input.