11 - Abstract Factory
Tags: flutter, dart, oop, programming
Published: 2021-03-29 06:46:55

In the last article, I have analysed the Factory Method design pattern. This time I would like to analyse and implement an OOP design pattern, which has a similar purpose, even a similar name, but is more flexible and suits the structure of big projects better than the Factory Method design pattern — it is the Abstract Factory.

## Table of Contents

* What is the Abstract Factory design pattern?
* Analysis
* Implementation
* Your Contribution

## What is the Abstract Factory design pattern?

![Santa's Factory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j8s292vixkva9xi2dcez.gif)

**Abstract Factory** is a **creational** design pattern, also known as **Kit**. Its intention in the [GoF book](https://en.wikipedia.org/wiki/Design_Patterns) is described as:

> Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

The main purpose of the Abstract Factory design pattern is to encapsulate creating a family of objects in a separate factory object, hence abstracting the process of object creation. For all supported families of objects, a common interface for creating a family of objects is defined, then a concrete factory class is created implementing this interface.

If you follow this series, I expect you have just had a sense of deja vu, haven’t you? The Factory Method design pattern has pretty much the same intention and purpose. Yes, it is true, simply like that. But why there is a separate factory pattern, then? The main difference between these two patterns is that the Abstract Factory pattern provides a way to create a **family of related objects** — a single factory is responsible for creating several objects. As a result, you don’t need to provide a separate factory for each specific class/component. In fact, you can consider the Factory Method design pattern as a subset of the Abstract Factory pattern — the Abstract Factory consists of several factory methods where each one of them creates only one specific object.

The Abstract Factory design pattern makes the creation of the objects more flexible:

* *Compile-time* flexibility — the way objects are created can be implemented and changed independently from clients by defining new (sub)classes;
* *Run-time* flexibility — a class can be configured with a factory object, which it uses to create objects, and even more, the factory object can be exchanged dynamically.

Finally, this pattern removes the direct binding of application-specific classes into the code. Correspondingly, the code only deals with interfaces of specific objects and their factories, but not with the specific implementations.

Let’s move to the analysis to understand the details of the Abstract Factory and how this design pattern could be implemented.

## Analysis

The general structure of the Abstract Factory design pattern looks like this:

![Abstract Factory Class Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d9y6rae343kuoaikvysz.png)

* *Abstract Factory* — declares an interface of operations that create abstract *Product* objects;
* *Concrete Factory* — implements the operations to create *Concrete Product* objects. **Each Concrete Factory corresponds only to a single variant of products**;
* *Product* — declares an interface for a type of *Product* object;
* *Concrete Product* — implements the *Product* interface and defines a product object to be created by the corresponding *Concrete Factory*;
* *Client* — uses only interfaces declared by the *Abstract Factory* and *Product* classes.

### Applicability

The usage of the Abstract Factory design pattern should be considered when a system’s code needs to work with various families of related objects (products), but it should not depend on the concrete classes of those products, on how they are created, composed and represented. The said design pattern provides an interface for the creation of objects from each class of the product family. By using this interface instead of concrete implementations of objects, the representation layer or the system’s code, in general, should not worry about creating the wrong variant of a product that does not match other objects from the family. This restriction is helpful when you want to introduce platform-specific widgets/UI components to the representation layer and keep the consistency across the whole system.

## Implementation

![Let's Do This](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0yiooh9xdg0m77gadu8s.gif)

If you have read the last article, you should already be familiar with the problem that could be resolved by using the factory design pattern. If not, here is a short overview of the problem we will fix:

> Even though you are using the same code base with Flutter, usually there is a demand that UI components should look different on different platforms. The simplest imaginable use case in Flutter context — showing the Material or Cupertino style widgets based on whether you are using accordingly an Android or iOS device.

Last time, we have resolved this issue by introducing the Factory Method design pattern to our code and providing a separate factory for each platform-specific component which creates the required widget dynamically after checking the current platform, hence separating the business logic from the representation layer (UI). However, this approach becomes a tremendous headache when multiple components exist which should look different on distinct platforms. Based on the Factory Method design pattern, every component should have a dedicated factory class to it and e.g. if you are creating an application for Android, iOS and Web, every new component would also require to add a new abstract class and 3 extra derived classes for the implementation in each specific platform.

Having the said problems in mind, the Abstract Factory design pattern is a better option than the Factory Method since only a single factory is needed per platform, a family of components are created and used together.

### Class diagram

The class diagram below shows the implementation of the **Abstract Factory** design pattern.

![Abstract Factory Implementation Class Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sp1anvk7lsc69nypiz0o.png)

*IWidgetsFactory* is an abstract class that is used as an interface for all the specific widget factories:

* *getTitle()* — an abstract method that returns the title of the factory. Used in the UI;
* *createActivityIndicator()* — an abstract method which returns the specific implementation (UI component/widget) of the activity (process) indicator implementing the *IActivityIndicator* interface;
* *createSlider()* — an abstract method which returns the specific implementation (UI component/widget) of the slider implementing the *ISlider* interface;
* *createSwitch()* — an abstract method that returns the specific implementation (UI component/widget) of the switch button implementing the *ISwitch* interface.

*MaterialWidgetsFactory* and *CupertinoWidgetsFactory* are concrete classes that implement the *IWidgetsFactory* class and its methods. *MaterialWidgetsFactory* creates Material style components (widgets) while the *CupertinoWidgetsFactory* creates Cupertino style widgets.

*IActivityIndicator*, *ISlider* and *ISwitch* are abstract classes that define the *render()* method for each component. These classes are implemented by both — Material and Cupertino — widgets.

*AndroidActivityIndicator*, *AndroidSlider* and *AndroidSwitch* are concrete implementations of the Material widgets implementing the *render()* method of corresponding interfaces.

*IosActivityIndicator*, *IosSlider* and *IosSwitch* are concrete implementations of the Cupertino widgets implementing the *render()* method of corresponding interfaces.

*AbstractFactoryExample* contains a list of factories implementing the *IWidgetsFactory* interface. After selecting the specific factory, the example widget uses its methods to create the corresponding widgets/UI components.

### IWidgetsFactory

An interface that defines methods to be implemented by the specific factory classes. These methods are used to create components (widgets) of the specific type defined by the concrete factory. Dart language does not support the interface as a class type, so we define an interface by creating an abstract class and providing a method header (name, return type, parameters) without the default implementation.

![iwidgets_factory.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfe7nav1sqel9907p15n.png)

### Widget factories

*MaterialWidgetsFactory* — a concrete factory class that implements the *IWidgetsFactory* interface and its methods creating the Material style widgets.

![material_widgets_factory.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hkm5qrfg7d0pnzc16sod.png)

*CupertinoWidgetsFactory* — a concrete factory class that implements the *IWidgetsFactory* interface and its methods creating the Cupertino style widgets.

![cupertino_widgets_factory.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x3ahgr1huzgsy2t19nu5.png)

### IActivityIndicator

An interface that defines the *render()* method to render the activity indicator component (widget).

![iactivity_indicator.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oho5fjhnxrr5h8csvn6d.png)

### Activity indicator widgets

*AndroidActivityIndicator* — a specific implementation of the activity indicator component returning the Material style widget *CircularProgressIndicator*.

![android_activity_indicator.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y8zow02ptzgu3szbyi3u.png)

*IosActivityIndicator* — a specific implementation of the activity indicator component returning the Cupertino style widget *CupertinoActivityIndicator*.

![ios_activity_indicator.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b3qsy8b8nirj3debpf6n.png)

### ISlider

An interface that defines the *render()* method to render the slider component (widget).

![islider.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/soi9q2no1pxghlxisphl.png)

### Slider widgets

*AndroidSlider* — a specific implementation of the slider component returning the Material style widget *Slider*.

![android_slider.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0n7jknr2o9lxa4sf18q5.png)

*IosSlider* — a specific implementation of the slider component returning the Cupertino style widget *CupertinoSlider*.

![ios_slider.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dxre1r8zmzsutub5fwyk.png)

### ISwitch

An interface that defines the *render()* method to render the switch component (widget).

![iswitch.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/abmbu7nx6vc3m5c1u1a3.png)

### Switch widgets

*AndroidSwitch* — a specific implementation of the switch button component returning the Material style widget *Switch*.

![android_switch.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ddjxfx3s4norkz7lexq5.png)

*IosSwitch* — a specific implementation of the switch button component returning the Cupertino style widget *CupertinoSwitch*.

![ios_switch.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fc9klqgdl5ywaohz52gg.png)

### Example

First of all, a markdown file is prepared and provided as a pattern’s description:

![Abstract Factory Markdown](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cmzv3cb5jzlmi84oudq6.gif)

*AbstractFactoryExample* contains a list of *IWidgetsFactory* objects (factories). After selecting the specific factory from the list, corresponding widgets are created using the factory methods and provided to the UI.

![abstract_factory_example.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jk91adqvithdpl4n4i6k.png)

As you can see in the *build()* method, the example widget does not care about the selected concrete factory as long as it implements the *IWidgetsFactory* interface which methods return components implementing the corresponding common interfaces among all the factories and providing the *render()* methods used in the UI. Also, the implementation of the specific widgets is encapsulated and defined in separate widget classes implementing the *render()* method. Hence, the UI logic is not tightly coupled to any factory or component class which implementation details could be changed independently without affecting the implementation of the UI itself.

![Abstract Factory Example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yfgj1uqewp70uvxgxymi.gif)

As you can see in the example, by selecting the specific platform widgets option, appropriate widgets are created by the factory methods and provided to the user.

All of the code changes for the Abstract Factory design pattern and its example implementation could be found [here](https://github.com/mkobuolys/flutter-design-patterns/pull/12).

## Your Contribution

💖 or 🦄 this article to show your support and motivate me to write better!
💬 Leave a response to this article by providing your insights, comments or wishes for the next topic.
📢 Share this article with your friends, colleagues on social media.
➕ Follow me on dev.to or any other social media platform.
⭐ Star the [Github](https://github.com/mkobuolys/flutter-design-patterns) repository.