1. Overview

The Abstract Factory is a software design pattern whose goal is to provide a single interface to create families of objects with the same theme but without exposing the concrete implementation. This pattern provides another layer of abstraction over the factory pattern.

In this tutorial, we’ll look at an efficient approach for the implementation of the Abstract Factory in Kotlin.

2. The Abstract Factory Pattern

The Abstract Factory is a creational design pattern and the main purpose is to encapsulate the creation of objects with a common theme in a separate factory object. In addition, each factory object has the responsibility of providing building services for all objects.

Let’s take a look at the component classes and interfaces of the Abstract Factory:

  • Abstract Factory: defines an interface of operations to create abstract objects
  • Concrete Factory: implements the operations to create concrete objects
  • Abstract Object: defines an interface for a specific type of object
  • Concrete Object: class definition of the object to be created by the Concrete Factory
  • Client: a class that uses the interfaces defined in Abstract Factory and Abstract Object

The following diagram shows the structure of an Abstract Factory along with its components:

AbstractFactory 1

3. Implementation

For our case of study, let’s consider the example of a family of weapons. For that, we’ll need a way to create objects of type Weapon that can be used on the client-side.

Let’s define the common generic interface for the type Weapon:

interface Weapon {

    fun use():String
}

Next, we’ll create the abstract factory class that will serve as the generic interface for creating objects of type Weapon:

abstract class WeaponFactory {

    abstract fun buildWeapon(): Weapon
}

For this tutorial, we’ll use companion objects to implement the specific factories. Companion objects in Kotlin allow creating objects declared in the same file as a class. For instance, we can define a companion object Factory for each specific weapon we might need.

Let’s now create the Crossbow class:

class Crossbow : Weapon {

    companion object Factory : WeaponFactory() {
        override fun buildWeapon() = Crossbow()
    }

    override fun use(): String {
        return "Using crossbow weapon"
    }
}

After that, we just need to define the final client for this type of weapon:

val factory : WeaponFactory = Crossbow.Factory
val crossbow = factory.buildWeapon()

assertNotNull(crossbow)
assertEquals("Using crossbow weapon", crossbow.use())

We can see that the Weapon type is defined at runtime through the Factory declaration.

Similarly, we can also define another type of Weapon. Let’s define a type of sword – the Katana:

class Katana : Weapon {

    companion object Factory : WeaponFactory() {
        override fun buildWeapon() = Katana()
    }

    override fun use(): String {
        return "Using katana weapon"
    }
}

4. Advantages of Abstract Factory

Implementing the Abstract Factory pattern can result in a more complicated code to maintain. However, there are some advantages of using this pattern:

  • The concrete objects classes have a loose coupling with the client, thus we’ll get better flexibility and reusability of code
  • The process of creating new types of objects is unified in a single interface, making the base code easier to maintain
  • It’s compliant with the Open/Closed Principle – therefore, we can extend the functionality by adding new factories without affecting the existing code

5. Conclusion

In this article, we’ve explored an efficient approach to implement the Abstract Factory pattern in Kotlin. This pattern is useful when we need another level of abstraction over a group of factories. In addition, we saw that Kotlin offers great support for implementing the pattern.

As always, the complete code for this article is available over on GitHub.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.