AbstractFactory
Abstract Factory is a creational design pattern that lets you produce families of related objects without specifying their concrete classes.
Problem
Imagine that you’re creating a furniture shop simulator. Your code consists of classes that represent:
- A family of related products, say:
Chair
+Sofa
+CoffeeTable
. - Several variants of this family. For example, products
Chair
+Sofa
+CoffeeTable
are available in these variants:Modern
,Victorian
,ArtDeco
.
Product families and their variants
You need a way to create individual furniture objects so that they match other objects of the same family. Customers get quite mad when they receive non-matching furniture.
A modern sofa doesn't match Victorian-style chairs
Also, you don’t want to change existing code when adding new products or families of products to the program. Furniture vendors update their catalogs very often, and you wouldn’t want to change the core code each time it happens.
Solution
The first thing the Abstract Factory pattern suggests is to explicitly declare interfaces for each distinct product of the product family (e.g., chair, sofa or coffee table). Then you can make all variants of products follow those interfaces. For example, all chair variants can implement the Chair
interface; all coffee table variants can implement the CoffeeTable
interface, and so on.
All variants of the same object must be moved to a single class hierarchy.
The next move is to declare the Abstract Factory—an interface with a list of creation methods for all products that are part of the product family (for example, createChair
, createSofa
and createCoffeeTable
). These methods must return abstract product types represented by the interfaces we extracted previously: Chair
, Sofa
, CoffeeTable
and so on.
Each concrete factory corresponds to a specific product variant.
Now, how about the product variants? For each variant of a product family, we create a separate factory class based on the AbstractFactory
interface. A factory is a class that returns products of a particular kind. For example, the ModernFurnitureFactory
can only create ModernChair
, ModernSofa
and ModernCoffeeTable
objects.
The client code has to work with both factories and products via their respective abstract interfaces. This lets you change the type of a factory that you pass to the client code, as well as the product variant that the client code receives, without breaking the actual client code.
The client shouldn’t care about the concrete class of the factory it works with.
Say the client wants a factory to produce a chair. The client doesn’t have to be aware of the factory’s class, nor does it matter what kind of chair it gets. Whether it’s a Modern model or a Victorian-style chair, the client must treat all chairs in the same manner, using the abstract Chair
interface. With this approach, the only thing that the client knows about the chair is that it implements the sitOn
method in some way. Also, whichever variant of the chair is returned, it’ll always match the type of sofa or coffee table produced by the same factory object.
There’s one more thing left to clarify: if the client is only exposed to the abstract interfaces, what creates the actual factory objects? Usually, the application creates a concrete factory object at the initialization stage. Just before that, the app must select the factory type depending on the configuration or the environment settings.
Structure
- Abstract Products declare interfaces for a set of distinct but related products which make up a product family.
- Concrete Products are various implementations of abstract products, grouped by variants. Each abstract product (chair/sofa) must be implemented in all given variants (Victorian/Modern).
- The Abstract Factory interface declares a set of methods for creating each of the abstract products.
- Concrete Factories implement creation methods of the abstract factory. Each concrete factory corresponds to a specific variant of products and creates only those product variants.
- Although concrete factories instantiate concrete products, signatures of their creation methods must return corresponding abstract products. This way the client code that uses a factory doesn’t get coupled to the specific variant of the product it gets from a factory. The Client can work with any concrete factory/product variant, as long as it communicates with their objects via abstract interfaces.
Applicability
- Use the Abstract Factory when your code needs to work with various families of related products, but you don’t want it to depend on the concrete classes of those products—they might be unknown beforehand or you simply want to allow for future extensibility.
- Consider implementing the Abstract Factory when you have a class with a set of Factory Methods that blur its primary responsibility.
Abstract Factory in Java
In this example, buttons and checkboxes will act as products. They have two variants: macOS and Windows.
The abstract factory defines an interface for creating buttons and checkboxes. There are two concrete factories, which return both products in a single variant.
Client code works with factories and products using abstract interfaces. It makes the same client code working with many product variants, depending on the type of factory object.
Button.java
:
1 | /** |
MacOSButton.java
:
1 | /** |
WindowsButton.java
:
1 | /** |
Checkbox.java
:
1 | /** |
MacOSCheckbox.java
:
1 | /** |
WindowsCheckbox.java
:
1 | /** |
GUIFactory.java
: Abstract Factory
1 | /** |
MacOSFactory.java
: Concrete Factory
1 | /** |
WindowsFactory.java
: Concrete Factory
1 | /** |
Application.java
: Client Code
1 | /** |
Demo.java
: App configuration
1 | /** |
Related Issues not found
Please contact @b1ngsha to initialize the comment