Niezmienny obiekt i budowniczy

Budowniczy a pola wymagane

W obecnej postaci kod pozwala na utworzenie obiektu bez inicjowania żadnego pola. Pierwszym rozwiązaniem jakie przychodzi na myśl jest dodanie parametrów konstruktora w klasie builder. Jest to dobre rozwiązanie jeżeli wymaganych parametrów jest niewiele, jeżeli jest ich więcej ponownie pojawia problem pomyłek podczas przekazywania wartości parametrów.

Aby wymusić inicjację wymaganych nie tracąc innych zalet poprzedniego rozwiązania, należy jeszcze bardziej rozbudować klasę Address, na początek klasa budowniczego oraz konstruktor klasy Address powinny stać się prywatne.

public class Address 
{
	// ...
	private Address(string addressLine1, string addressLine2, string zipCode, string city, string stateArea, string country)
	//...

	private class Builder 
	{
		// ...
	}
}

Następnie należy zadeklarować szereg interfejsów, odpowiadających poszczególnym, obowiązkowym, polom klasy, dodatkowy interfejs pozwalający skonfigurować pola opcjonalne i zaimplementować wszystkie interfejsy w klasie Builder, w przykładzie poniżej zdecydowałem się na implementację dosłowną (implicit).

public class Address 
{
	// ...
	public interface IConfigureAddressLine1 
	{
		IConfigureZipCode AddressLine1(string addressLine1);
	}

	public interface IConfigureZipCode
	{
		IConfigureCity ZipCode(string zipCode);
	}

	public interface IConfigureCity
	{
		IBuilder City(string city);
	}

	public interface IBuilder
	{
		IBuilder AddressLine2(string addressLine2);
		IBuilder StateArea(string stateArea);
		IBuilder Country(string country);
		Address Build();
	}

	private class Builder : IConfigureAddressLine1, IConfigureZipCode, IConfigureCity, IBuilder
	{

		// ...
		IConfigureZipCode IConfigureAddressLine1.AddressLine1(string addressLine1)
		{
			return AddressLine1(addressLine1);
		}

		IConfigureCity IConfigureZipCode.ZipCode(string zipCode)
		{
			return ZipCode(zipCode);
		}

		IBuilder IConfigureCity.City(string city)
		{
			return City(city);
		}

		IBuilder IBuilder.AddressLine2(string addressLine2)
		{
			return AddressLine2(addressLine2);
		}

		IBuilder IBuilder.StateArea(string stateArea)
		{
			return StateArea(stateArea);
		}

		IBuilder IBuilder.Country(string country)
		{
			return Country(country);
		}
	}
}

Aby ponownie umożliwić utworzenie instancji klasy należy dodać metodę fabrykującą:

public class Address 
{
	public static IConfigureAddressLine1 CreateNew()
	{
		return new Builder();
	}
	 // ...
}

Na tym etapie IDE prowadzi użytkownika za rączkę podczas tworzenia nowej instancji klasy.

var address = Address
    .CreateNew()
    .AddressLine1("Konwaliowa 31/20")
    .ZipCode("20036")
    .City("Lublin")
    // Opcjonalnie
    // .StateArea("Lubelskie")
    // .Country("Polska")
    // .AddressLine2("Druga klatka")
    .Build();

Ostatnim krokiem jest dodanie interfejsu ICloneBuilder, pozwalającej zmienić wartość dowolnego pola, zaimplementowanie go w klasie Builder i dodanie metody Clone do klasy Address.

public class Address 
{
	// ...
	public ICloneBuilder Clone()
	{
		return new Builder(this);
	}
	// ...
	private class Builder : ICloneBuilder, IConfigureAddressLine1, IConfigureZipCode, IConfigureCity, IBuilder
	{
                // ...
		ICloneBuilder ICloneBuilder.AddressLine1(string addressLine1)
		{
			return AddressLine1(addressLine1);
		}

		ICloneBuilder ICloneBuilder.ZipCode(string zipCode)
		{
			return ZipCode(zipCode);
		}

		ICloneBuilder ICloneBuilder.City(string city)
		{
			return City(city);
		}

		ICloneBuilder ICloneBuilder.AddressLine2(string addressLine2)
		{
			return AddressLine2(addressLine2);
		}

		ICloneBuilder ICloneBuilder.StateArea(string stateArea)
		{
			return StateArea(stateArea);
		}

		ICloneBuilder ICloneBuilder.Country(string country)
		{
			return Country(country);
		}
                // ...
	}
}
var templateAddress = Address.CreateNew()
    .AddressLine1("Konwaliowa 31/20")
    .ZipCode("20036")
    .City("Lublin")
    .StateArea("Lubelskie")
    .Country("Polska")
    .Build();

var modifedAddress = templateAddress.Clone()
    .AddressLine1("Konwaliowa 31/21")
    .Build();

Jedna odpowiedź do “Niezmienny obiekt i budowniczy”

  1. Pingback: dotnetomaniak.pl

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *