Design Patterns: Abstract Factory – with Kotlin Examples

It’s been a while since my last post, but now I am back and I will write a series of posts every week on a new topic, which is Design Patterns.

In this series of posts, my colleges and I will talk about design patterns, which introduced in the famous book “Design Patterns: Elements of Reusable Object-Oriented Software” by the Gang of Four. This book will be our reference for this series.

Just a heads up, Design patterns are kind of an advanced topic and they use objects oriented programming concepts such as inheritance, polymorphism .. etc, and also it’s gonna be a long post, so buckle up.

The first pattern that I will talk about is the Abstract Factory pattern (also known as Kia), and it’s from creational patterns family.

The intent of the Abstract Factory as the book defines it:

“Provide an interface for creating families of related or dependent objects without specifying their concrete classes”.

So what is that mean? Let me give you a use case where the Abstract Factory is applicable.

Consider that you build an application that works in multiple operating systems, such as Windows, Mac, and Linux, let’s say that you want to create widgets like scroll bars, buttons .. etc, that support these operating systems. So for an application to be portable across different operating systems its widgets should not be hard-coded for a particular OS. instantiating classes of widgets specific to a particular OS throughout the application makes it hard to change for a different OS later.

To solve this issue we can define an abstract factory for widgets; let’s call it WidgetFactory class. This class declares an interface for creating each basic kind of widget. On the side of the widget itself, there will be also an abstract class for each kind of widget, and concrete subclasses that implement the widget interface for each specific OS. The WidgetFactory’s interface has an operation that returns a new widget object for each abstract widget class. Clients (i.e. users of the class) call these operations to obtain widget instances, but they aren’t aware of the concrete classes they’re using.

To understand what I just said, let’s look at the diagram below

As the diagram shows, for each OS there is a concrete subclass of WidgetFactory and each subclass implements the operations to create the appropriate widget for the corresponding OS. For example, the CreateButton operation on the LinuxWidgetFactory instantiates and returns a Linux button, the same for windows return a windows button and for Mac return a Mac button. Clients create widgets solely through the WidgetFactory interface without having any knowledge of the particular concrete classes that implement theses widgets for a particular OS.

So returning to the intent of this pattern, “Providing an interface (WidgetFactory) for creating families of related or dependent objects (ScrollBar, Button) without specifying their concrete classes (LinuxScrollBar, LinuxButton)”. There is still a part of the intent I didn’t explain it yet, the part of (creating families of related or dependent objects). The WidgetFactory also enforces dependencies between the concrete widget classes, which means that the Linux scroll bar should be used with the Linux button, and that constrain is enforced automatically as a consequence of using the LinuxWidgetFactory. Hence, “creating families of related or dependent objects” comes in place.

Here is the structure of the Abstract Factory pattern:

The players of the Abstract Factory as the diagram above shows are:

  • AbstractFactory:
    • Declare the interface for operations that create abstract product objects. Which corresponds to the WidgetFactory in our previous example.
  • ConcreteFactory:
    • Implement the operations in the abstract factory to create concrete product objects. Which correspond to LinuxWidgetFactory, WindowesWidgetFactory, and MacWidgetFactory
  • AbstractProduct:
    • Declare an interface for the specific type of product object. Which corresponds to ScrollBar and Button.
  • ConcreteProduct:
    • Define a product object to be created by the corresponding concrete factory and also implement the AbstractProduct interface, which corresponds to LinuxScrollBar and LinuxButton, WindowsScrollBar and WindowsButton, MacScrollBar and MacButton.
  • Client:
    • Uses only the interfaces declared by the AbstractFactory and AbstractProduct classes.

Normally a single instance of ConcreteFactory class is created at run-time. This concrete factory creates product objects having a particular implementation. In our case, LinuxWidgetFactory creates widget objects having the implementation that suitable for Linux.

To create different product objects, clients should use a concrete factory. In our case, the clients should use either a WindowesWidgetFactory or MacWidgetFactory.

Another point to highlight that the AbstractFactory defers the creation of product objects to its ConcreteFactory subclass. (It will be more clear when we come to the code)

So enough talking and show me the code.

hint: The language that will be using is Kotlin

The goal for our simple application is to show the enemy ship info. We have an interface EnemyShipFactory that abstracts two concrete classes. One of the factory classes is ZaraEnemyShipFactory that create Zara enemy ship. The other factory is KiraEnemyShipFactory that create Kiara enemy ship.

interface EnemyShipFactory {
    fun createZaraShip(): EnemyShip
}

class ZaraEnemyShipFactory: EnemyShipFactory {
    override fun createZaraShip(): EnemyShip {
        return ZaraEnemyShip()
    }

}

class KiraEnemyShipFactory : EnemyShipFactory {
    override fun createZaraShip(): EnemyShip {
        return KiraEnemyShip()
    }
}

 

The EnemyShip interface also abstract two concrete classes, ZaraEnemyShip and KiraEnemyShip

interface EnemyShip {
    fun showEnemyShipInfo()
}

class ZaraEnemyShip : EnemyShip {
    override fun showEnemyShipInfo() {
      println("1. Ship name: Zara.\n2. Weapon damage: 20.\n3. Ship speed: 100 km/h")
    }
}

class KiraEnemyShip: EnemyShip {
    override fun showEnemyShipInfo() {
        println("1. Ship name: Kira.\n2. Weapon damage: 70.\n3. Ship speed: 60 km/h")
    }
}

 

Here is a complete working program.

interface EnemyShipFactory {
    fun createZaraShip(): EnemyShip
}

class ZaraEnemyShipFactory: EnemyShipFactory {
    override fun createZaraShip(): EnemyShip {
        return ZaraEnemyShip()
    }

}

class KiraEnemyShipFactory : EnemyShipFactory {
    override fun createZaraShip(): EnemyShip {
        return KiraEnemyShip()
    }
}

interface EnemyShip {
    fun showEnemyShipInfo()
}

class ZaraEnemyShip : EnemyShip {
    override fun showEnemyShipInfo() {
        println("1. Ship name: Zara.\n2. Weapon damage: 20.\n3. Ship speed: 100 km/h")
    }
}

class KiraEnemyShip: EnemyShip {
    override fun showEnemyShipInfo() {
        println("1. Ship name: Kira.\n2. Weapon damage: 70.\n3. Ship speed: 60 km/h")
    }
}

fun main(args: Array<String>) {
    val zaraEnemyShipFactory = ZaraEnemyShipFactory()
    val kiraEnemyShipFactory = KiraEnemyShipFactory()

    val zaraEnemyShip = zaraEnemyShipFactory.createZaraShip()
    val kiraEnemyShip = kiraEnemyShipFactory.createZaraShip()

    zaraEnemyShip.showEnemyShipInfo()
    println()
    kiraEnemyShip.showEnemyShipInfo()
}

 

The output will be as follows:

1. Ship name: Zara.
2. Weapon damage: 20.
3. Ship speed: 100 km/h

1. Ship name: Kira.
2. Weapon damage: 70.
3. Ship speed: 120 km/h

 

The AbstractFactory classes often implemented with factory methods, but they also can be implemented using the Prototype pattern. Also, a concrete factory is often to be a singleton.

We will be talked about these patterns in future posts.

Here we reach to the end of this post but there is much more to this pattern and you can refer to the book to know more about when to use this pattern, benefits and liabilities to use it and some useful techniques for implementing the patters and more.

So that’s all I have for this week and I always appreciate feedback and If you have any question please put your questions in the comments.

Till the next week and as the Green brothers always say “Don’t forget to be awesome”.

2 thoughts on “Design Patterns: Abstract Factory – with Kotlin Examples

Leave a Reply

Your email address will not be published. Required fields are marked *