W kolejnym poście poświęconym wzorcom projektowym, zajmiemy się rozszerzeniem wzorca Factory Method. W definicji tego wzorca możemy przeczytać: Abstract Factory udostępnia interfejs pozwalający na tworzenie rodzin produktów. Spójrzmy na diagram UML:
Interfejs AbstractFactory definiuje interfejs, który musi być implementowany przez konkretne fabryki tworzące produkty (obiekty).
Concrete Factories odpowiadają za utworzenie konkretnych produktów.
Abstract Product definiuje interfejs rodziny produktów.
Product - produkt żądany przez klienta.
Przejdźmy do przykładu:
Przyjmijmy że mamy dwie piekarnie. Jedna zlokalizowana jest w Krakowie druga w Warszawie. Obie pieką placki kruche i z owocami. Piekarnia w Krakowie piecze placek z jabłkami i śliwkami. Piekarnia w Warszawie piecze placki z wiśniami i truskawkami. Przyjrzyjmy się temu opisowi od strony projektanta oprogramowania. Klient który zamawia placki ma możliwość zamówienia placka od dwóch różnych dostawców (piekarnie Kraków i Warszawa). W każdej z piekarń produkuje się placki o różnych składach a to co je łączy, to to, że są to placki typu Kruchy i Owocowy. Te informacje są wystarczające do zbudowania wzorca Abstract Factory. (Dodałem jeszcze pole price zawierające cenę placka - nie używałem go jednak; można samemu dodać np. wyświetlanie ceny po wywołaniu metody opis).
A więc przejdźmy do sedna czyli kodu :) :
1 public abstract class Bakery
2 {
3 public abstract IFruitPie CreateFruitPie();
4 public abstract ITartPie CreateTartPie();
5 }
6
7 public class KrakowBakery : Bakery
8 {
9 public override IFruitPie CreateFruitPie()
10 {
11 return new ApplePie(23.50);
12 }
13
14 public override ITartPie CreateTartPie()
15 {
16 return new PlumPie(28.40);
17 }
18 }
19
20 public class WarsawBakery : Bakery
21 {
22 public override IFruitPie CreateFruitPie()
23 {
24 return new CherryPie(33.68);
25 }
26
27 public override ITartPie CreateTartPie()
28 {
29 return new StrawberryPie(39.40);
30 }
31 }
32
33 public interface IFruitPie
34 {
35 string Description();
36 double Price { get; }
37 }
38
39 public interface ITartPie
40 {
41 string Description();
42 double Price { get; }
43 }
44
45 public class PlumPie : ITartPie
46 {
47 private double _price;
48
49 public PlumPie(double price)
50 {
51 this._price = price;
52 }
53 #region ITartPie Members
54 public string Description()
55 {
56 return "Tart Plum Pie";
57 }
58
59 public double Price
60 {
61 get { return this._price; }
62 }
63
64 #endregion
65 }
66
67 public class StrawberryPie : ITartPie
68 {
69 private double _price;
70
71 public StrawberryPie(double price)
72 {
73 this._price = price;
74 }
75 #region ITartPie Members
76 public string Description()
77 {
78 return "Tart Strawberry Pie";
79 }
80
81 public double Price
82 {
83 get { return this._price; }
84 }
85
86 #endregion
87 }
88
89 public class ApplePie : IFruitPie
90 {
91 private double _price;
92
93 public ApplePie(double price)
94 {
95 this._price = price;
96 }
97 #region IFruitPie Members
98
99 public string Description()
100 {
101 return "Pie with fresh Apples";
102 }
103
104 public double Price
105 {
106 get { return _price; }
107 }
108
109 #endregion
110 }
111
112 public class CherryPie : IFruitPie
113 {
114 private double _price;
115
116 public CherryPie(double price)
117 {
118 this._price = price;
119 }
120 #region IFruitPie Members
121
122 public string Description()
123 {
124 return "Cherry Pie with fresh Cherries";
125 }
126
127 public double Price
128 {
129 get { return _price; }
130 }
131
132 #endregion
133 }
134
135 class Program
136 {
137 static void Main(string[] args)
138 {
139 Bakery bakery = new KrakowBakery();
140 IFruitPie FruitPie = bakery.CreateFruitPie();
141 Console.WriteLine(FruitPie.Description());
142 ITartPie TartPie = bakery.CreateTartPie();
143 Console.WriteLine(TartPie.Description());
144
145 Console.WriteLine("---------");
146
147 bakery = new WarsawBakery();
148 FruitPie = bakery.CreateFruitPie();
149 Console.WriteLine(FruitPie.Description());
150 TartPie = bakery.CreateTartPie();
151 Console.WriteLine(TartPie.Description());
152 }
153 }
Może kilka słów o implementacji.
1 - 5 - definicja abstrakcyjnej klasy Abstract Factory
7 - 31 - definicje konkretnych fabryk
33 - 43 - interfejsy odpowiedzialne za typy produktów
44 - 132 - definicje konkretnych produktów (placków)
Powyższa implementacja może zostać ulepszona poprzez np. użycie typów generycznych - dzięki temu tworzymy wzorzec do generowania konkretnych fabryk czy produktów (mniej pisania).
Kiedy stosować Abstract Factory?
- generowanie widoku i interfejsu aplikacji;
- interakcja z systemami operacyjnymi (w każdym mamy takie operacje jak otwórz, zapisz, wyświetl - tyle, że w Windowsie jest to inaczej realizowane niż np w Linuxie);
- kiedy znamy cały zestaw tworzonych produktów (dodanie nowego wymaga zmiany we wszystkich fabrykach)
Co zyskujemy dzięki Abstract Factory?
- łatwa zmiana całych "rodzin produktów" (np. zmiana skórek w aplikacji)
- ukrycie implementacji przed klientem
- logiczna spójność systemu - fabryka odpowiada za tworzenie odpowiednich produktów
Brak komentarzy:
Prześlij komentarz