Hvordan designe korrekt kode i C# - Del 1
Published: 2020-04-28 07:43:36

Det virker som en uungåelig skjebne for mange prosjekter at de ender opp like, med forsinkelser, uløste feil og en komplett uforståelig kodebase. Det til tross for at vi alle er kompetente og bringer med oss masse erfaring. 

Jeg tror den grunnleggende årsaken til det er at vi mangler en generell teori for det vi driver med. Uten en slik teori er det vanskelig å argumentere for hva som er korrekt og vi faller gjerne tilbake på personlige preferanser og erfaring. 

Men om vi analyserer preferansene våre, kan vi klare å finne noen egenskaper som korrekt kode må inneha, og noen enkle prinsipper til å veilede oss? 

Denne artikkelserien er mitt forsøk på det. Jeg definerer min versjon av korrekt kode, etablerer noen prinsipper, og demonstrerer en designprosess som jeg synes fungerer bra.

# Definisjon

Korrekt kode har disse egenskapene:

- Eliminerer feil
- Ekskluderer ugyldige tilstander
- Er lett å resonnere over
- Promoterer korrekt bruk


# Prinsipper

Vi ønsker korrekt kode fordi det gir oss noen garantier som gjør det lettere å skrive feilfri kode. Garantiene vi vil ha er:

- Et objekt er alltid korrekt
- Et objekt kan ikke brukes feil

Dette utleder noen prinsipper:

1. Objekter instansieres med en *factory*. 
2. Et instansiert objekt er *immutable*.
3. Vår minste byggestein er et *value object*.
4. Vi tillater ikke bruk av `null`.

## Factory

*Factory* er verktøyet vårt for å konstruere korrekte objekter. En *factory* returnerer en korrekt instans eller feiler i forsøket:

```
var divisor = NonZeroNumber.Of(10);

class NonZeroNumber
{
	static NonZeroNumber Of(decimal value)
	{
		if (value == 0) throw new InvalidArgumentException("Cannot be 0");
		return new NonZeroNumber {Value = value};
	}
}
```

## Immutable

Når vi først har et garantert korrekt objekt, så risikerer vi ikke å ødelegge den garantien ved å tillate endringer. Objektene våre er *immutable*:

```
var divisor = NonZeroNumber.Of(10);
divisor.Value = 0; // compile error

class NonZeroNumber
{
	public decimal Value { get; private set; }
}
```

## Value objects

Vi bruker ikke primitive typer direkte, fordi det verken kan garantere korrekt verdi eller bruk. Primitiver kan beskrive en mengde, men ikke av hva. Derfor vil den minste byggesteinen vår være et *value object*:

```
var dividend = Number.Of(10);
var divisor = NonZeroNumber.Of(5);
var quotiend = Divide(dividend, divisor);

Number Divide (Number dividend, NonZeroNumber divisor)
{
	return Number.Of(dividend.Value / divisor.Value);
}
```

## Ingen bruk av null

Om vi skal garantere korrekte objekter som ikke kunne brukes feil, kan vi ikke tillate bruk av `null`. Når ekskluderer bruk `null` blir vi tvunget til å revurdere hvordan vi modellerer fraværet av verdi. 

Vi kan velge å uttrykke det eksplisitt:

```
var user = User.None;
```

Eller å uttrykke det med flere tilstander:

```
var newCar = RegisteredCar.Of(registrationNumber, owner);
var usedCar = newCar.ReregisterOn(newOwner);

abstract class Car
{
	RegistrationNumber Number { get; private set; }
	Owner Owner { get; private set; }
}

class RegisteredCar : Car
{
	static RegisteredCar Of(RegistrationNumber number, Owner owner)
	{
		return new RegisteredCar
		{
			RegistrationNumber = number,
			Owner = owner
		}
	}
	
	ReregisteredCar ReregisterOn(Owner newOwner)
	{
		return ReregisteredCar.Of(this, newOwner);
	}
}

class ReregisteredCar : Car
{
	static ReregisteredCar Of(RegisteredCar car, Owner owner)
	{
		return new ReregisteredCar
		{
			RegistrationNumber = car.RegistrationNumber,
			Owner = owner,
			PreviousOwners = new []{ car.Owner }
		}
	}

	Owner[] PreviousOwners { get; private set; }
}
```

# Veien videre

Hittil har jeg definert noen egenskaper jeg mener korrekt kode må ha, argumentert for at det finnes prinsipper vi må følge, og antydet at det finnes en enkel designprosess.

I neste artikkel vil jeg bruke en typisk chat som problemstilling, der jeg starter med en typisk og naiv modell. Jeg kommer til å vise hvordan denne modellen kan forbedres ved å følge prosessen og anvende prinsippene jeg har presentert her.

# Relevante lenker

[Towards general theories of software engineering](https://doi.org/10.1016/j.scico.2014.11.005)
[Making Defects Impossible](https://killalldefects.com/2019/08/29/making-defects-impossible/)
[Entities and Value Objects in C# for DDD](https://conductofcode.io/post/entities-and-value-objects-in-csharp-for-ddd/)