Abstract Factory Pattern
2024-11-07  / 23 Design Patterns  / 1. Creational Pattern

0. Overview

Abstract Factory Pattern: is defined as a design pattern that provides an interface for creating a group of related or interdependent objects, allowing the client to access these objects without specifying their concrete classes. This way, clients can obtain various products of the same family at different levels without knowing the specifics of the products’ classes.

The Abstract Factory Pattern is a type of creational pattern and can be considered an advanced version of the Factory Method Pattern. In the Factory Method Pattern, the focus is on one product level structure, while the Abstract Factory Pattern deals with multiple product level structures. When a factory structure can create an entire family of objects across different product level structures, the Abstract Factory Pattern becomes simpler and more efficient compared to the Factory Method Pattern.

  • Product Family: Refers to a family of products that are functionally related and belong to different product level structures; it is a group of products produced by the same factory, each belonging to a distinct product level. For example, Huawei’s family includes a phone, laptop, and earphones, while Apple’s family includes a phone, laptop, and earphones. Both families represent three product levels: phone, laptop, and earphones.
  • Product Class (Product Level): This refers to a product’s inheritance structure. For instance, if the abstract product is a phone, its subclasses might include Huawei and Apple phones. The abstract phone and its specific brands constitute a product level structure, with the abstract phone as the parent class and specific brand phones as its subclasses.

The diagram below illustrates the relationship between product families and product levels. The horizontal axis represents product levels, while the vertical axis represents product families. There are two product families (Huawei Factory, Apple Factory), each with three product levels (phone, laptop, earphones). By identifying the product family and its level, a product can be uniquely determined.

abstract_factory_product_concept

From the diagram above, we can see that each concrete factory can produce all the products belonging to one product family, so the Abstract Factory Pattern only requires two concrete factories. In contrast, using the Factory Method Pattern for six concrete products would require six concrete factories, increasing complexity and the number of classes in the system.

1. Structure and Implementation

Like the Factory Method Pattern, the Abstract Factory Pattern consists of four main elements: abstract factory, concrete factory, abstract product, and concrete product. However, the Abstract Factory Pattern differs in the number of methods within the abstract factory and the number of abstract products.

The main roles are as follows:

  • Abstract Factory (IFactory): Provides an interface for creating a series of products, containing multiple product creation methods such as makePhone(), makeLaptop(), and makeEarphone(), allowing the creation of products across different levels.
  • Concrete Factory (HuaweiFactory, AppleFactory): Implements the methods defined in the abstract factory to create specific products within its product family.
  • Abstract Product (IProduct, AbstractPhone, AbstractLaptop, AbstractEarphone): Defines product specifications, describing the main characteristics and functions of the products.
  • Concrete Product (HuaweiPhone, HuaweiLaptop, HuaweiEarphone, ApplePhone, AppleLaptop, AppleEarphone): Implements the interfaces defined by the abstract product role. Each concrete product is created by a corresponding concrete factory.

The class diagram is as follows:

2. Code Example

2.1. Abstract Products and Concrete Products

Abstract product interfaces and product abstract classes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Abstract Product
public interface IProduct {
/**
* Product description
*/
void desc();
}

// Phone Abstract Product
public abstract class AbstractPhone implements IProduct {

@Override
public abstract void desc();
}

// Laptop Abstract Product
public abstract class AbstractLaptop implements IProduct {

@Override
public abstract void desc();
}

// Earphone Abstract Product
public abstract class AbstractEarphone implements IProduct {

@Override
public abstract void desc();
}

Concrete product implementations for the Huawei family:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Huawei Concrete Earphone Product
public class HuaweiPhone extends AbstractPhone {

@Override
public void desc() {
System.out.println("I am Huawei phone.");
}
}

// Huawei Concrete Laptop Product
public class HuaweiLaptop extends AbstractLaptop {

@Override
public void desc() {
System.out.println("I am Huawei laptop.");
}
}

// Huawei Concrete Earphone Product
public class HuaweiEarphone extends AbstractEarphone {

@Override
public void desc() {
System.out.println("I am Huawei earphone.");
}
}

Concrete product implementations for the Apple family:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Apple Concrete Phone Product
public class ApplePhone extends AbstractPhone {

@Override
public void desc() {
System.out.println("I am Apple phone.");
}
}

// Apple Concrete Laptop Product
public class AppleLaptop extends AbstractLaptop {

@Override
public void desc() {
System.out.println("I am Apple laptop.");
}
}

// Apple Concrete Earphone Product
public class AppleEarphone extends AbstractEarphone {

@Override
public void desc() {
System.out.println("I am Apple earphone.");
}
}

2.2. Abstract Factory and Concrete Factory

Abstract factory interface:

1
2
3
4
5
6
7
8
9
// Abstract Factory: create different products
public interface IFactory {

IProduct makePhone();

IProduct makeLaptop();

IProduct makeEarphone();
}

Huawei concrete factory implementation subclass: This class implements the creation of Huawei product family items.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* Huawei Concrete Factory
*/
public class HuaweiFactory implements IFactory {

public HuaweiFactory() {
System.out.println("Create Huawei factory.");
}

/**
* produce Huawei phones
*/
@Override
public IProduct makePhone() {
System.out.println("Make Huawei phone...");
return new HuaweiPhone();
}

/**
* produce Huawei laptops
*/
@Override
public IProduct makeLaptop() {
System.out.println("Make Huawei laptop...");
return new HuaweiLaptop();
}

/**
* produce Huawei earphones
*/
@Override
public IProduct makeEarphone() {
System.out.println("Make Huawei earphone...");
return new HuaweiEarphone();
}
}

Apple concrete factory implementation subclass: This class implements the creation of Apple product family items.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* Apple Concrete Factory
*/
public class AppleFactory implements IFactory {

public AppleFactory() {
System.out.println("Create Apple factory.");
}

/**
* produce Apple phones
*/
@Override
public IProduct makePhone() {
System.out.println("Make Apple phone...");
return new ApplePhone();
}

/**
* produce Apple laptops
*/
@Override
public IProduct makeLaptop() {
System.out.println("Make Apple laptop...");
return new AppleLaptop();
}

/**
* produce Apple earphones
*/
@Override
public IProduct makeEarphone() {
System.out.println("Make Apple earphone...");
return new AppleEarphone();
}
}

2.3. Usage Example

Example code for using the Abstract Factory pattern:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class AbstractFactoryMain {
public static void main(String[] args) {
// Create a Huawei factory to produce concrete products.
IFactory huaweiFactory = new HuaweiFactory();
// Create a phone through the Huawei factory.
IProduct huaweiPhone = huaweiFactory.makePhone();
huaweiPhone.desc();
// Create a laptop through the Huawei factory.
IProduct huaweiLaptop = huaweiFactory.makeLaptop();
huaweiLaptop.desc();
// Create an earphone through the Huawei factory.
IProduct huaweiEarphone = huaweiFactory.makeEarphone();
huaweiEarphone.desc();

System.out.println("-----------------");

// Create a Apple factory to produce concrete products.
IFactory appleFactory = new AppleFactory();
// Create a phone through the Apple factory.
IProduct applePhone = appleFactory.makePhone();
applePhone.desc();
// Create a laptop through the Apple factory.
IProduct appleLaptop = appleFactory.makeLaptop();
appleLaptop.desc();
// Create an earphone through the Apple factory.
IProduct appleEarphone = appleFactory.makeEarphone();
appleEarphone.desc();
}
}

Execution result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Create Huawei factory.
Make Huawei phone...
I am Huawei phone.
Make Huawei laptop...
I am Huawei laptop.
Make Huawei earphone...
I am Huawei earphone.
-----------------
Create Apple factory.
Make Apple phone...
I am Apple phone.
Make Apple laptop...
I am Apple laptop.
Make Huawei earphone...
I am Huawei earphone.

3. Application in Source Code

3.1. JDK - javax.xml.transform.TransformerFactory

TransformerFactory as an abstract factory defines two abstract methods for creating different objects: Transformer newTransformer() and Templates newTemplates(). Its concrete implementation classes, TransformerFactoryImpl and SmartTransformerFactoryImpl, override and implement these methods to create the corresponding objects.

3.2. JDK - java.sql.Connection

The java.sql.Connection interface is analogous to the abstract factory interface, as it provides (produces) different product levels such as Statement, PreparedStatement, and CallableStatement. The products provided are all concrete products under the java.sql.Statement interface.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface Connection extends Wrapper, AutoCloseable {
// ...

// Return a regular SQL executor: A regular, parameter-less query SQL (each execution requires SQL to be compiled).
Statement createStatement() throws SQLException;

// Return a SQL executor with pre-compilation functionality: Supports SQL with variable parameters (pre-compiles the SQL to prevent SQL injection).
PreparedStatement prepareStatement(String sql) throws SQLException;

// Return a SQL executor for executing stored procedures: Supports calling stored procedures and provides support for output and input/output parameters.
CallableStatement prepareCall(String sql) throws SQLException;

// ...
}

Class diagram of the Abstract Factory and its concrete factory implementations:

4. Summary

Applicable Scenarios:
The Abstract Factory pattern was initially applied to create window components for different operating systems, such as the Button and Text components in Java’s AWT, where the native implementations differ between Windows and UNIX. In this case, different systems represent product families, and different components represent product levels.

  1. A system should not depend on the details of how instances of product classes are created, composed, and represented. This is important for all types of factory patterns.
  2. When the products to be created are a series of related or interdependent product families, such as the television, washing machine, air conditioner, etc., in an appliance factory.
  3. When there are multiple product families in the system, but only one family of products is used at a time, meaning only products from a single family are consumed. For example, someone may prefer using products from a particular ecosystem, such as the Xiaomi ecosystem or the Apple ecosystem.
  4. When the system provides a library of product classes, with all products appearing through the same interface, ensuring that the client is independent of the implementation.

Advantages:

  1. Separation of Interface and Implementation: The client uses the abstract factory to create the required objects, without knowing the specific implementation. The client programs against the product interfaces, meaning the client is decoupled from the specific product implementations.
  2. Easy to Switch Product Families: Because a concrete factory implementation represents a product family, for example, switching from the Huawei family to the Apple family only requires replacing the concrete factory.

Disadvantages:
When adding a new factory, such as Xiaomi, you only need to add a new Xiaomi concrete factory class and implement the corresponding product methods, which offers good extensibility. However, when adding a new product, you need to modify the abstract factory, which leads to changes in all factory implementation classes.

References