Prototyp – Wzorzec Projektowy (Prototype)

Prototyp (ang. Prototype) – wzorzec projektowy. Przykład zastosowania oparty na przykładzie w celu łatwiejszego zrozumienia tego wzorca dla laika.

James miał firmę, która zajmowała się składaniem i sprzedawaniem hulajnóg. Hulajnoga składa się z czterech elementów: kierownicy, kolek, hamulca i naklejki. Klient miał możliwość stworzenia swojej własnej hulajnogi, która składała się z elementów pochodzących od rożnych producentów. Klient miał możliwość samemu wybrać sobie z jakich elementów chciałby, żeby jego hulajnoga się składała. Andrzej chce kupić hulajnogę. Andrzej zażyczył sobie, aby jego hulajnoga miała szwajcarska kierownice, niemieckie hamulce, chińska naklejkę i polskie kolka. W celu zamówienia spersonalizowanej hulajnogi Andrzej musi wypełnić formularz z jakich elementów ma się składać hulajnoga.

KrajPochodzeniaProduktu kierownica = KrajPochodzeniaProduktu.Szwajcaria;
KrajPochodzeniaProduktu naklejka = KrajPochodzeniaProduktu.Chiny;
KrajPochodzeniaProduktu hamulce = KrajPochodzeniaProduktu.Niemcy;
KrajPochodzeniaProduktu kolka = KrajPochodzeniaProduktu.Polska;
var formularz = new Formularz(kierownica, naklejka, hamulce, kolka);
NiestandardowaHulajnoga hulajnoga = formularz.WyprodukujHulajnoge();

Wszystko oczywiście powędrowało do odpowiedniego formularza.

public class Formularz
{
	private KrajPochodzeniaProduktu _kierownica;
	private KrajPochodzeniaProduktu _naklejka;
	private KrajPochodzeniaProduktu _hamulce;
	private KrajPochodzeniaProduktu _kolka;

	public Formularz(KrajPochodzeniaProduktu kierownica, KrajPochodzeniaProduktu naklejka,
			KrajPochodzeniaProduktu hamulce, KrajPochodzeniaProduktu kolka)
	{
		_kierownica = kierownica;
		_naklejka = naklejka;
		_hamulce = hamulce;
		_kolka = kolka;
	}

	public NiestandardowaHulajnoga WyprodukujHulajnoge()
	{
		var hulajnoga = new NiestandardowaHulajnoga(_kierownica, _naklejka, _hamulce, _kolka);
		return hulajnoga;
	}
}
public enum KrajPochodzeniaProduktu
{
	Polska,
	Chiny,
	Niemcy,
	Wlochy,
	Szwajcaria,
	Norwegia,
	Irlandia
}
public class NiestandardowaHulajnoga
{
	private KrajPochodzeniaProduktu _kierownica;
	private KrajPochodzeniaProduktu _naklejka;
	private KrajPochodzeniaProduktu _hamulce;
	private KrajPochodzeniaProduktu _kolka;

	public NiestandardowaHulajnoga(KrajPochodzeniaProduktu kierownica, KrajPochodzeniaProduktu naklejka, 
		KrajPochodzeniaProduktu hamulce, KrajPochodzeniaProduktu kolka)
	{
		_kierownica = kierownica;
		_naklejka = naklejka;
		_hamulce = hamulce;
		_kolka = kolka;
	}
}

Zadowolony Andrzej wraca do domu z swoją hulajnogą.

Andrzej wrócił jednak po tygodniu do Jamesa ze swoją hulajnogą. Tak mu się spodobała jego hulajnoga, że chce mieć jeszcze jedną taką samą hulajnogę. Więc na nowo powinien wypełnić formularz.

Prototyp – Czy można to zrobić bez wypełniania formularza?

Tak, można. Można wyprodukować nową hulajnogę bazując na już istniejącej. W tym celu można stworzyć metodę Clone w NiestandardowejHulajnodze, która zwraca taki sam obiekt – z tymi samymi wartościami w polach.

public class NiestandardowaHulajnoga
{
	...

	public NiestandardowaHulajnoga Clone()
	{
		var sklonowanaHulajnoga = new NiestandardowaHulajnoga(_kierownica, _naklejka, _hamulce, _kolka);
		return sklonowanaHulajnoga;
	}

	public override string ToString()
	{
		return String.Format("Niestandardowa hulajnoga: \nKierownica: {0} \nNaklejka: {1} \nHamulce: {2} \nKoła: {3}", _kierownica, _naklejka, _hamulce, _kolka);
	}
}

Użycie tego jest bardzo proste:

KrajPochodzeniaProduktu kierownica = KrajPochodzeniaProduktu.Szwajcaria;
KrajPochodzeniaProduktu naklejka = KrajPochodzeniaProduktu.Chiny;
KrajPochodzeniaProduktu hamulce = KrajPochodzeniaProduktu.Niemcy;
KrajPochodzeniaProduktu kolka = KrajPochodzeniaProduktu.Polska;
var formularz = new Formularz(kierownica, naklejka, hamulce, kolka);
NiestandardowaHulajnoga hulajnoga = formularz.WyprodukujHulajnoge();

var nowaHulajnoga = hulajnoga.Clone();
Console.WriteLine(nowaHulajnoga.ToString());

Niestandardowa hulajnoga:
Kierownica: Szwajcaria
Naklejka: Chiny
Hamulce: Niemcy
Koła: Polska

Prototyp – Zróbmy to profesjonalnej

Dodajmy interface do metody Clone.

public interface ICloneable
{
	object Clone();
}

Poprawmy klasę NiestandrardowejHulajnogi.

public class NiestandardowaHulajnoga : ICloneable
{
	...

	public object Clone()
	{
		var sklonowanaHulajnoga = new NiestandardowaHulajnoga(_kierownica, _naklejka, _hamulce, _kolka);
		return sklonowanaHulajnoga;
	}
}

Skopiowaliśmy hulajnogę! Super. Ale można zrobić to jeszcze ładniej. MemberwiseClone jest metodą, którą dostarcza nam .NET. Metoda ta klonuje swoją instancę.

public class NiestandardowaHulajnoga : ICloneable
{
	...

	public object Clone()
	{
		var sklonowanaHulajnoga = this.MemberwiseClone();
		return sklonowanaHulajnoga;
	}
}

W naszym przykładzie metoda MemberwiseClone tworzy nową instancję NiestandardowejHulajnogi – czyli obiekt sklonowanaHulajowa. Następnie kopiuje wartości z oryginalnego obiektu na którym metoda Clone została wywołana do pól w nowo powstałej instancji – czyli wartości z kierownicy, naklejki itd.

Jednak MemberwiseCloce działa tak jakbyśmy chcieli dla:

  • Typów prostych jak np. int, byte.
  • String (spójrz linki na dole)
  • Enumeratory
  • Proste struktury, które pola posiadają tylko typy wyżej wymienione

Wszystko o czym do tej pory czytałeś odnosi się do shallow coping.

 

A co z klasami? Co jeżeli jakieś pole to referencja do  klasy? – ktoś zapytał. W tym momencie kopiowana jest referencja. Nie tworzony jest nowy obiekt klasy podrzędnej. Jeżeli chcielibyśmy się bardziej klonowaniem pól z klasami to  mamy do czynienia z deep coping.

 

Prototyp – przydatne linki

Klonowanie wg Microsoftu

Dlaczego shallow copying działa dla stringa?

Czy używanie Icloneable ma sens?

Leave a Comment

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