Factory Method Design Pattern in Kotlin

1. Definition

The Factory Method Design Pattern provides an interface for creating objects in a superclass, but allows subclasses to alter the types of objects that will be created.

2. Problem Statement

When you have a base class with multiple subclasses and based on input, you need to return one class out of several possible classes, the straightforward approach might introduce tight coupling and does not support open/closed principle.

3. Solution

The Factory Method pattern suggests that you replace direct object construction calls (using the new operator) with calls to a special factory method.

4. Real-World Use Cases

1. GUI libraries where each OS provides a different implementation of a button or window.

2. Payment gateway integrations where each provider has a different implementation.

5. Implementation Steps

1. Create an interface or abstract class for the products.

2. Concrete product classes implement this interface.

3. Create an abstract creator class that contains the abstract factory method.

4. Concrete creators implement this factory method to produce products.

6. Implementation in Kotlin

// Step 1: Product Interface
interface Product {
    fun describe(): String
}

// Step 2: Concrete Products
class ConcreteProductA : Product {
    override fun describe() = "I'm product A"
}

class ConcreteProductB : Product {
    override fun describe() = "I'm product B"
}

// Step 3: Abstract Creator with Factory Method
abstract class Creator {
    abstract fun factoryMethod(): Product

    fun someOperation(): String {
        val product = factoryMethod()
        return "Creator: ${product.describe()}"
    }
}

// Step 4: Concrete Creators
class ConcreteCreatorA : Creator() {
    override fun factoryMethod(): Product = ConcreteProductA()
}

class ConcreteCreatorB : Creator() {
    override fun factoryMethod(): Product = ConcreteProductB()
}

fun main() {
    val creatorA = ConcreteCreatorA()
    println(creatorA.someOperation())  // Using product A

    val creatorB = ConcreteCreatorB()
    println(creatorB.someOperation())  // Using product B
}

Output:

Creator: I'm product A
Creator: I'm product B

Explanation:

1. We start with a Product interface which defines a describe method.

2. ConcreteProductA and ConcreteProductB are implementations of the Product interface.

3. The Creator abstract class provides a factory method factoryMethod and a method someOperation which uses the product created by the factory method.

4. Concrete creators ConcreteCreatorA and ConcreteCreatorB override the factory method to return their respective products.

5. In the main function, we create instances of concrete creators and call their someOperation method which internally utilizes the factory method.

7. When to use?

Use the Factory Method pattern when:

1. You need to provide a generalized interface to create particular concrete instances.

2. The exact type of the product object isn't known until runtime.

3. The creation process is complex or requires a lot of configurations.

4. You want to localize the knowledge of which class gets created.

By using the Factory Method, we can introduce new types of products without changing the existing client code, hence promoting the open/closed principle.


Comments