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. This pattern is useful when a system needs to be independent of how its objects are created, composed, and represented, especially when there are multiple families of products involved.
Applicability
- You need to ensure consistency among products used together
- You want to isolate client code from concrete classes
- You have multiple families of products and need to switch between them
Pros and Cons
Advantages
- Ensures consistency among related products
- Products created by a factory are guaranteed to be compatible with each other.
- Isolates concrete classes
- The client works only with interfaces or abstract classes, not specific implementations.
- Supports product variants easily
- Easily switch between product families by changing the factory object.
- Encourages separation of concerns
- Object creation logic is kept separate from business logic.
Drawbacks
- **Adding new product types is hard
- If you need a new product in each family, you must update every factory class.
- Can result in many small classes
- Each product family and factory needs its own set of interfaces and classes.
Approach
- Define Abstract Product interfaces for each kind of product.
- Define Concrete Products that implement the abstract product interfaces.
- Define an Abstract Factory interface with creation methods for each abstract product.
- Define Concrete Factories that implement the abstract factory interface, producing specific families of products.
- Clients use the abstract factory to create products without knowing their concrete types.
Components
- AbstractProductA, AbstractProductB
- Interfaces for a family of related products.
- ConcreteProductA1, ConcreteProductA2, etc.
- Concrete implementations of the abstract products.
- AbstractFactory
- Declares creation methods for each type of product.
- ConcreteFactory1, ConcreteFactory2
- Implement the creation methods to return product variants.
classDiagram class AbstractProductA { <<abstract>> +operation() } class ConcreteProductA1 { +operation() } class AbstractProductB { <<abstract>> +operation() } class ConcreteProductB1 { +operation() } class AbstractFactory { <<abstract>> +create_product_a(): AbstractProductA +create_product_b(): AbstractProductB } class ConcreteFactory1 { +create_product_a(): ConcreteProductA1 +create_product_b(): ConcreteProductB1 } AbstractProductA <|-- ConcreteProductA1 AbstractProductB <|-- ConcreteProductB1 AbstractFactory <|-- ConcreteFactory1 ConcreteFactory1 --> ConcreteProductA1 : creates ConcreteFactory1 --> ConcreteProductB1 : creates
Example
Let’s say we’re building a UI toolkit that should support multiple operating systems like Windows and macOS. Each OS has its own set of GUI components.
Abstract factory
interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
Concrete factory
class WindowsFactory implements GUIFactory {
public Button createButton() {
return new WindowsButton();
}
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}
class MacOSFactory implements GUIFactory {
public Button createButton() {
return new MacOSButton();
}
public Checkbox createCheckbox() {
return new MacOSCheckbox();
}
}
Abstract product
interface Button {
void paint();
}
interface Checkbox {
void paint();
}
Concrete product
class WindowsButton implements Button {
public void paint() {
System.out.println("Rendering a Windows-style button.");
}
}
class MacOSButton implements Button {
public void paint() {
System.out.println("Rendering a macOS-style button.");
}
}
class WindowsCheckbox implements Checkbox {
public void paint() {
System.out.println("Rendering a Windows-style checkbox.");
}
}
class MacOSCheckbox implements Checkbox {
public void paint() {
System.out.println("Rendering a macOS-style checkbox.");
}
}
Client
class Application {
private Button button;
private Checkbox checkbox;
public Application(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
}
public void render() {
button.paint();
checkbox.paint();
}
}
Initialisation
public class AbstractFactoryExample {
public static void main(String[] args) {
GUIFactory factory;
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("mac")) {
factory = new MacOSFactory();
} else {
factory = new WindowsFactory();
}
Application app = new Application(factory);
app.render();
}
}
OUTPUT (on Windows):
Rendering a Windows-style button.
Rendering a Windows-style checkbox.
OUTPUT (on macOS):
Rendering a macOS-style button.
Rendering a macOS-style checkbox.
Back to parent page: Creational Patterns
Design_Pattern Creational_Design_Patterns Abstract_Factory_Pattern SOFT2201