Abstract Factory Design Pattern in Scala

1. Definition

The Abstract Factory Design Pattern is a creational pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes.

2. Problem Statement

Sometimes, systems need to be independent from how its objects are created, composed, and represented, and the system should be configured with multiple families of objects.

3. Solution

The Abstract Factory pattern suggests defining multiple factory methods for each type of object to be created. All these factory methods are combined inside a single interface, the abstract factory. Then, for each family of objects, we implement this interface.

4. Real-World Use Cases

1. UI libraries where each OS provides a different implementation of multiple UI elements like buttons, windows, and sliders.

2. Configurable set of products, such as computer configurations with different types of processors, memories, and hard drives.

5. Implementation Steps

1. Define separate interfaces for each type of product.

2. Define an abstract factory interface that declares multiple factory methods, one for each type of product.

3. Implement concrete factories for each product family.

4. Use the factory to create products.

6. Implementation in Scala Programming

// Abstract products
trait Button {
  def click(): String
}
trait Window {
  def open(): String
}
// Abstract factory
trait GUIFactory {
  def createButton(): Button
  def createWindow(): Window
}
// Concrete products for Windows OS
class WindowsButton extends Button {
  def click(): String = "WindowsButton clicked"
}
class WindowsWindow extends Window {
  def open(): String = "WindowsWindow opened"
}
// Concrete products for MacOS
class MacOSButton extends Button {
  def click(): String = "MacOSButton clicked"
}
class MacOSWindow extends Window {
  def open(): String = "MacOSWindow opened"
}
// Concrete factories
class WindowsFactory extends GUIFactory {
  def createButton(): Button = new WindowsButton()
  def createWindow(): Window = new WindowsWindow()
}
class MacOSFactory extends GUIFactory {
  def createButton(): Button = new MacOSButton()
  def createWindow(): Window = new MacOSWindow()
}
// Client code
object Main extends App {
  val factory: GUIFactory = if (args.contains("Windows")) new WindowsFactory() else new MacOSFactory()
  val button: Button = factory.createButton()
  val window: Window = factory.createWindow()
  println(button.click())
  println(window.open())
}

Output:

WindowsButton clicked
WindowsWindow opened
or
MacOSButton clicked
MacOSWindow opened

Explanation:

1. Button and Window are abstract products.

2. GUIFactory is the abstract factory that declares the creation methods for these products.

3. For each product family (Windows and MacOS in this case), we have concrete implementations of both products (WindowsButton, WindowsWindow, MacOSButton, MacOSWindow) and factories (WindowsFactory, MacOSFactory).

4. The client code demonstrates how to use the abstract factory to create products. Depending on the input argument, a different family of products is instantiated.

7. When to use?

The Abstract Factory pattern is useful when:

1. The system needs to be independent from how its objects are created, composed, and represented.

2. The system is composed of multiple families of objects and needs to only use one of these families at a time.

3. You want to provide a library of objects without exposing the implementation details.

It provides a high level of flexibility but can introduce complexity with the addition of new products or families of products.


Comments