The abstract factory

The abstract factory is another design pattern from the family of factory patterns. The purpose is the same as all factory design patterns—to encapsulate the object creation logic and hide it from the user. The difference is how it is implemented.

The abstract factory design pattern relies on object composition in contrast to inheritance, which is used by the factory method. Here we have a separate object, which provides an interface to create instances of the classes we need.

Class diagram

Let's keep using the preceding SimpleConnection example here. The following diagram shows how the abstract factory is structured:

Class diagram

As we can see from the preceding figure, now we have a hierarchy of factories rather than a method inside our database client. We will be using the abstract DatabaseConnectorFactory in our application and it will be returning the right objects, depending on the actual instance type.

Code example

Let's have a look at our example from the source point of view. The following code listing shows the factory hierarchy:

trait DatabaseConnectorFactory {
  def connect(): SimpleConnection
}

class MySqlFactory extends DatabaseConnectorFactory {
  override def connect(): SimpleConnection = new SimpleMysqlConnection
}

class PgSqlFactory extends DatabaseConnectorFactory {
  override def connect(): SimpleConnection = new SimplePgSqlConnection
}

We can then use our factory by passing it to a class, which will call the required methods. Here is an example similar to the one we showed for the factory method design pattern:

class DatabaseClient(connectorFactory: DatabaseConnectorFactory) {
  def executeQuery(query: String): Unit = {
    val connection = connectorFactory.connect()
    connection.executeQuery(query)
  }
}

Let's see an example that uses our database client:

object Example {
  def main(args: Array[String]): Unit = {
    val clientMySql: DatabaseClient = new DatabaseClient(new MySqlFactory)
    val clientPgSql: DatabaseClient = new DatabaseClient(new PgSqlFactory)
    clientMySql.executeQuery("SELECT * FROM users")
    clientPgSql.executeQuery("SELECT * FROM employees")
  }
}

The following screenshot shows the output of this program:

Code example

This is how the abstract factory design pattern works. If we need to add another database client to our application, we can achieve it by adding another class, which extends DatabaseConnectionFactory. This is nice because it makes refactoring and extending easy.

Scala alternatives

This design pattern can also be achieved using different approaches. The fact that we use object composition to pass a factory to our class indicates that we can do something else: we can simply pass a function, just because in Scala they are a part of unification and they are treated the same way as objects.

What is it good for?

As with all factories, the details of object creation are hidden. The abstract factory design pattern is particularly useful when we want to expose families of objects (for example, database connectors). The clients then become decoupled from the concrete classes. This pattern is commonly shown as an example in different UI toolkits, where elements differ for different operating systems. It is also quite testable because we can provide mocks instead of an actual factory to the clients.

Even though the incompatibility problem that we mentioned previously is still present here, it is somewhat harder to encounter now. This is mainly because here the client will actually just pass one single factory as a parameter. And in the case where we provide the user with concrete factories, everything is already taken care of when these factories were written.

What is it not so good for?

It could be problematic if the objects and methods we are using (SimpleConnection in our case) change signatures. In some cases, this pattern could also complicate our code unnecessarily and make it unreadable and hard to follow.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.246.223