Summary: in this tutorial, you’ll learn how to use the C# abstract factory pattern to create families of dependent objects without specifying their concrete classes.
Introduction to the C# Abstract Factory design pattern
The Abstract Factory is a creational design pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes.
The Abstract Factory pattern is useful when you need to create families of related objects that work seamlessly with each other as a group and have consistent behavior.
Let’s say you are building a UI toolkit for creating cross-platform mobile applications. And you want to create UI widgets like buttons, and text fields that work on both iOS and Android platforms.
In this case, you can use the Abstract Factory pattern to create an abstract factory interface that defines methods for building each of these widgets. And you then implement this interface with concrete factories that create widgets on each platform.
The advantage of using the Abstract Factory pattern is that it encapsulates the creation of objects and separates the object creation logic from the rest of your code. This allows you to easily change the implementation of the objects without affecting the code base.
For example, you want to support additional mobile platforms besides iOS and Android.
Abstract Factory UML diagram
The following UML diagram illustrates the abstract factory design pattern:
The Abstract Factory pattern involves the following participants:
- Abstract Factory: This is the interface that declares a set of factory methods for creating abstract products. It is responsible for defining the operations that create the families of related objects.
- Concrete Factory: These are the classes that implement the abstract factory interface and create families of related objects. Each concrete factory is responsible for creating a different variant of the product family.
- Abstract Product: This is the interface that declares a set of operations that the concrete products must implement. It defines a type of product but does not provide the implementation details.
- Concrete Product: These are the classes that implement the abstract product interface and provide a specific implementation of the product. Each concrete factory creates a different variant of the product family.
- Client: This is the class that uses the abstract factory and abstract product interfaces to create families of related objects. The client code does not know which concrete products it is creating, only that they belong to the same family of products.
The Client uses the abstract factory interface to create a family of related objects.
The abstract factory creates the concrete factories that create the concrete products. Also, the Client uses the abstract product interface to create concrete products, without knowing the specific implementation details.
C# Abstract Factory design pattern examples
The following C# program demonstrates how to use the Abstract Factory design pattern by creating different families of widgets for iOS and Android platforms:
using static System.Console;
namespace AbstractFactory;
// Widget abstract class
public abstract class Widget
{
public abstract void Render();
}
// Button widget for iOS platform
public class IOSButton : Widget
{
public override void Render()
{
WriteLine("Rendering iOS button...");
}
}
// TextField widget for iOS platform
public class IOSTextField : Widget
{
public override void Render()
{
WriteLine("Rendering iOS text field...");
}
}
// Button widget for Android platform
public class AndroidButton : Widget
{
public override void Render()
{
WriteLine("Rendering Android button...");
}
}
// TextField widget for Android platform
public class AndroidTextField : Widget
{
public override void Render()
{
WriteLine("Rendering Android text field...");
}
}
// Widget factory interface
public interface IWidgetFactory
{
public Widget CreateButton();
public Widget CreateTextField();
}
// iOS widget factory
public class IOSWidgetFactory : IWidgetFactory
{
public Widget CreateButton()
{
return new IOSButton();
}
public Widget CreateTextField()
{
return new IOSTextField();
}
}
// Android widget factory
public class AndroidWidgetFactory : IWidgetFactory
{
public Widget CreateButton()
{
return new AndroidButton();
}
public Widget CreateTextField()
{
return new AndroidTextField();
}
}
// App (Client) that to create and use widgets
public class App
{
private readonly IWidgetFactory factory;
public App(IWidgetFactory factory)
{
this.factory = factory;
}
public void RenderUI()
{
Widget button = factory.CreateButton();
Widget textField = factory.CreateTextField();
button.Render();
textField.Render();
}
}
public class Program
{
public static void Main(string[] args)
{
WriteLine("Enter the platform (ios or android): ");
var platform = ReadLine().ToLower();
IWidgetFactory factory;
if (platform == "ios")
{
factory = new IOSWidgetFactory();
}
else if (platform == "android")
{
factory = new AndroidWidgetFactory();
}
else
{
WriteLine("Invalid platform entered.");
return;
}
var app = new App(factory);
app.RenderUI();
}
}
Code language: C# (cs)
How it works.
First, define an abstract class called Widget
that represents a generic widget. The Widget
class has an abstract method Render()
that renders the widget on a specific platform:
public abstract class Widget
{
public abstract void Render();
}
Code language: C# (cs)
Second, define two concrete classes, IOSButton
and IOS
for creating TextField
Button
and TextField
on the iOS platform. Both classes inherit from the Widget
class:
// Button widget for iOS platform
public class IOSButton : Widget
{
public override void Render()
{
WriteLine("Rendering iOS button...");
}
}
// TextField widget for iOS platform
public class IOSTextField : Widget
{
public override void Render()
{
WriteLine("Rendering iOS text field...");
}
}
Code language: C# (cs)
Third, define two similar concrete classes, AndroidButton
and Android
, for creating TextField
Button
and TextField
widgets on the Android platform. These classes also inherit from the Widget
class.
// Button widget for Android platform
public class AndroidButton : Widget
{
public override void Render()
{
WriteLine("Rendering Android button...");
}
}
// TextField widget for Android platform
public class AndroidTextField : Widget
{
public override void Render()
{
WriteLine("Rendering Android text field...");
}
}
Code language: C# (cs)
Fourth, define an interface IWidgetFactory
for creating Button
and TextField
widgets:
// Widget factory interface
public interface IWidgetFactory
{
public Widget CreateButton();
public Widget CreateTextField();
}
Code language: C# (cs)
Fifth, define a class called IOSWidgetFactory
that implements the IWidgetFactory
. The IOSWIdgetFactory
is responsible for creating Button
and TextField
widgets on the iOS platform:
// iOS widget factory
public class IOSWidgetFactory : IWidgetFactory
{
public Widget CreateButton()
{
return new IOSButton();
}
public Widget CreateTextField()
{
return new IOSTextField();
}
}
Code language: C# (cs)
Sixth, define the AndroidWidgetFactory
class that implements the IWidgetFactory
interface. The AdroidWidgetFactory
is in charge of creating Button
and TextField
widgets on the Android platform:
// Android widget factory
public class AndroidWidgetFactory : IWidgetFactory
{
public Widget CreateButton()
{
return new AndroidButton();
}
public Widget CreateTextField()
{
return new AndroidTextField();
}
}
Code language: C# (cs)
Seventh, define the App
class that takes an instance of the IWidgetFactory
interface and uses it to create and use widgets:
// App (Client) that to create and use widgets
public class App
{
private readonly IWidgetFactory factory;
public App(IWidgetFactory factory)
{
this.factory = factory;
}
public void RenderUI()
{
Widget button = factory.CreateButton();
Widget textField = factory.CreateTextField();
button.Render();
textField.Render();
}
}
Code language: C# (cs)
Eighth, the Main()
method of the Program
class prompts the user to enter the platform they want to create widgets for and then uses the appropriate concrete factory to create the widgets. It then creates an App
object and passes the factory to render the widget to it:
public class Program
{
public static void Main(string[] args)
{
WriteLine("Enter the platform (ios or android): ");
var platform = ReadLine().ToLower();
IWidgetFactory factory;
if (platform == "ios")
{
factory = new IOSWidgetFactory();
}
else if (platform == "android")
{
factory = new AndroidWidgetFactory();
}
else
{
WriteLine("Invalid platform entered.");
return;
}
var app = new App(factory);
app.RenderUI();
}
}
Code language: C# (cs)
The following shows the output of the program when you enter iOS as the platform:
Enter the platform (ios or android):
ios
Rendering iOS button...
Rendering iOS text field...
Code language: plaintext (plaintext)
If you enter Android as the platform, you’ll see the output like this:
Enter the platform (ios or android):
android
Rendering Android button...
Rendering Android text field...
Code language: plaintext (plaintext)
Here’s how the classes and interfaces in the program map to the participants in the Abstract Factory pattern:
- Abstract Factory: The
IWidgetFactory
interface. - Concrete Factory: The
IOSWidgetFactory
andAndroidWidgetFactory
classes. - Abstract Product: The
Widget
abstract class. - Concrete Product: The
IOSButton
,IOSTextField
,AndroidButton
, andAndroidTextField
classes. - Client: The
App
class.
Summary
- The Abstract Factory pattern is a creational design pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes.
- The Abstract Factory pattern is useful when a system needs to be independent of the way the objects it creates are generated or composed.
- The Abstract Factory pattern promotes loose coupling by allowing a client to create objects without needing to know the specific classes of objects it creates.