In my second article on design patterns, I am going to give you a quick overview of the "Abstract Factory pattern". Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
The Abstract Factory pattern is one level of abstraction higher than the factory pattern. One of the nice things about abstraction is that it lets you "take care" of the bigger picture and you only have to worry about the "details" later. The great advantage is that you are able to rely upon some other class to fill in the details for you.
You can use this pattern when you want to return one of several related classes of objects, each of which can return several different objects on request. In other words, the Abstract Factory is a factory object that returns one of several factories.
The code below demonstrates the Abstract Factory pattern creating parallel hierarchies of objects. Object creation has been abstracted and there is no need for hard-coded class names in the client code.
1: // Abstract Factory pattern - Structural example
2:
3: using System;
4:
5: namespace MyApp.AbstractFactory.Structural
6: {
7: /// <summary>
8: /// MainApp startup class for Structural
9: /// Abstract Factory Design Pattern.
10: /// </summary>
11: internal class MainApp
12: {
13: /// <summary>
14: /// Entry point into console application.
15: /// </summary>
16: public static void Main()
17: {
18: // Abstract factory #1
19: AbstractFactory factory1 = new ConcreteFactory1();
20: Client client1 = new Client(factory1);
21: client1.Run();
22:
23: // Abstract factory #2
24: AbstractFactory factory2 = new ConcreteFactory2();
25: Client client2 = new Client(factory2);
26: client2.Run();
27:
28:
29: // Wait for user input
30: Console.ReadKey();
31: }
32: }
33:
34: /// <summary>
35: /// The 'AbstractFactory' abstract class
36: /// </summary>
37: internal abstract class AbstractFactory
38: {
39: public abstract AbstractProductA CreateProductA();
40: public abstract AbstractProductB CreateProductB();
41: }
42:
43: /// <summary>
44: /// The 'ConcreteFactory1' class
45: /// </summary>
46: internal class ConcreteFactory1 : AbstractFactory
47: {
48: public override AbstractProductA CreateProductA()
49: {
50: return new ProductA1();
51: }
52:
53: public override AbstractProductB CreateProductB()
54: {
55: return new ProductB1();
56: }
57: }
58:
59: /// <summary>
60: /// The 'ConcreteFactory2' class
61: /// </summary>
62: internal class ConcreteFactory2 : AbstractFactory
63: {
64: public override AbstractProductA CreateProductA()
65: {
66: return new ProductA2();
67: }
68:
69: public override AbstractProductB CreateProductB()
70: {
71: return new ProductB2();
72: }
73: }
74:
75: /// <summary>
76: /// The 'AbstractProductA' abstract class
77: /// </summary>
78: internal abstract class AbstractProductA
79: {
80: }
81:
82: /// <summary>
83: /// The 'AbstractProductB' abstract class
84: /// </summary>
85: internal abstract class AbstractProductB
86: {
87: public abstract void Interact(AbstractProductA a);
88: }
89:
90: /// <summary>
91: /// The 'ProductA1' class
92: /// </summary>
93: internal class ProductA1 : AbstractProductA
94: {
95: }
96:
97: /// <summary>
98: /// The 'ProductB1' class
99: /// </summary>
100: internal class ProductB1 : AbstractProductB
101: {
102: public override void Interact(AbstractProductA a)
103: {
104: Console.WriteLine(this.GetType().Name +
105: " interacts with " + a.GetType().Name);
106: }
107: }
108:
109: /// <summary>
110: /// The 'ProductA2' class
111: /// </summary>
112: internal class ProductA2 : AbstractProductA
113: {
114: }
115:
116: /// <summary>
117: /// The 'ProductB2' class
118: /// </summary>
119: internal class ProductB2 : AbstractProductB
120: {
121: public override void Interact(AbstractProductA a)
122: {
123: Console.WriteLine(this.GetType().Name +
124: " interacts with " + a.GetType().Name);
125: }
126: }
127:
128: /// <summary>
129: /// The 'Client' class. Interaction environment for the products.
130: /// </summary>
131: internal class Client
132: {
133: private AbstractProductA _abstractProductA;
134: private AbstractProductB _abstractProductB;
135:
136: // Constructor
137:
138: public Client(AbstractFactory factory)
139: {
140: _abstractProductB = factory.CreateProductB();
141: _abstractProductA = factory.CreateProductA();
142: }
143:
144: public void Run()
145: {
146: _abstractProductB.Interact(_abstractProductA);
147: }
148: }
149: }
Output:
ProductB1 interacts with ProductA1
ProductB2 interacts with ProductA2
The Usage of this pattern makes it possible to interchange concrete classes without changing the code that uses them, even at runtime! However, employment of this pattern, as with similar design patterns, may result in unnecessary complexity and extra work in the initial writing of code. Used correctly the "extra work" pays off in the second instance of using the Factory.