Abstract factory pattern
Tags: designpatterns, programming, tutorial, java
Published: 2023-10-14 18:27:15

Let's explore the abstract factory pattern with a real-world example of developing software. Imagine we're building a drawing application where users can create various shapes, such as circles and squares. We want to implement different rendering modes: "2D" for regular rendering and "3D" for rendering shapes in a 3D environment. The abstract factory pattern can help us design this software in a flexible and maintainable way.

**Abstract Factory Pattern Overview:**
The abstract factory pattern provides an interface (or abstract class) for creating families of related or dependent objects without specifying their concrete classes. In our case, the abstract factory defines methods for creating different types of shapes and rendering modes.

Here's how we can apply the abstract factory pattern:

1. **Abstract Factory Interface:**
   We create an abstract factory interface called `ShapeFactory`. This interface declares methods for creating shapes and rendering modes.

   ```java
   interface ShapeFactory {
       Shape createShape();
       RenderingMode createRenderingMode();
   }
   ```

2. **Concrete Factories:**
   We implement two concrete factories: `2DShapeFactory` and `3DShapeFactory`. Each factory is responsible for creating shapes and rendering modes suitable for its respective context.

   ```java
   class 2DShapeFactory implements ShapeFactory {
       @Override
       public Shape createShape() {
           return new Circle(); // Create 2D shapes like circles
       }

       @Override
       public RenderingMode createRenderingMode() {
           return new TwoDRenderingMode(); // Use 2D rendering mode
       }
   }

   class 3DShapeFactory implements ShapeFactory {
       @Override
       public Shape createShape() {
           return new Sphere(); // Create 3D shapes like spheres
       }

       @Override
       public RenderingMode createRenderingMode() {
           return new ThreeDRenderingMode(); // Use 3D rendering mode
       }
   }
   ```

3. **Shapes and Rendering Modes:**
   We create concrete implementations of shapes and rendering modes. For example:

   ```java
   interface Shape {
       void draw();
   }

   class Circle implements Shape {
       @Override
       public void draw() {
           System.out.println("Drawing a circle");
       }
   }

   class Sphere implements Shape {
       @Override
       public void draw() {
           System.out.println("Drawing a sphere");
       }
   }

   interface RenderingMode {
       void render();
   }

   class TwoDRenderingMode implements RenderingMode {
       @Override
       public void render() {
           System.out.println("Using 2D rendering mode");
       }
   }

   class ThreeDRenderingMode implements RenderingMode {
       @Override
       public void render() {
           System.out.println("Using 3D rendering mode");
       }
   }
   ```

4. **Client Code:**
   In our drawing application, the client code can choose a factory based on the desired rendering mode (2D or 3D) and then use that factory to create shapes and rendering modes:

   ```java
   public class DrawingApp {
       public static void main(String[] args) {
           // User selects 2D rendering
           ShapeFactory factory2D = new 2DShapeFactory();
           Shape shape2D = factory2D.createShape();
           RenderingMode renderingMode2D = factory2D.createRenderingMode();

           shape2D.draw();
           renderingMode2D.render();

           // User selects 3D rendering
           ShapeFactory factory3D = new 3DShapeFactory();
           Shape shape3D = factory3D.createShape();
           RenderingMode renderingMode3D = factory3D.createRenderingMode();

           shape3D.draw();
           renderingMode3D.render();
       }
   }
   ```

In this example:

- The client code selects a factory (either 2D or 3D) based on the user's preference.
- The chosen factory creates shapes and rendering modes that are compatible with the selected rendering mode.
- The client code can work with these shapes and rendering modes without worrying about their concrete implementations.

**Benefits of the Abstract Factory Pattern:**
- **Flexibility**: We can easily switch between 2D and 3D rendering modes without modifying the client code. Adding new rendering modes or shapes is also straightforward.
- **Maintainability**: The abstract factory pattern promotes code maintainability because it encapsulates the creation logic for related objects.
- **Modularity**: Each factory deals with a specific family of objects, promoting modularity and separation of concerns in the codebase.

**Difference b/w Factory Pattern and the Abstract Factory Pattern**:

**Factory Pattern:**

Imagine you're writing a computer program, and you need to create different types of shapes, such as circles and rectangles.

- In the Factory Pattern, you have a "Shape Factory" class that knows how to create these shapes.
- When your program needs a shape, you ask the Shape Factory to create it for you.
- The Factory Pattern helps you create individual objects (shapes) without worrying about how they're constructed. It's like using a vending machine to get a snack; you select what you want, and the vending machine gives it to you without you needing to know how it's made.

**Abstract Factory Pattern:**

Now, let's say your program has expanded, and you're not just working with shapes; you're also dealing with different colors for those shapes, like red and blue. You want to make sure that when you create a shape, you can also set its color consistently.

- This is where the Abstract Factory Pattern comes in. It helps you create not just shapes but also colors and makes sure they go well together and have a consistent style.
- You have an "Artistic Factory" that knows how to create both shapes and colors with an artistic theme.
- When your program needs a shape with a specific color, you ask the Artistic Factory to create it for you.
- The Abstract Factory Pattern ensures that the shape and color match and belong to the same artistic style.


In summary, the abstract factory pattern allows us to create families of related objects in a way that's flexible and easy to maintain. This pattern is valuable when dealing with scenarios where different sets of objects need to work together seamlessly, like in our drawing application example.

😍 If you enjoy the content, please 👍 like, 🔄 share, and 👣 follow for more updates!