Scala Tutorial

Basics

Control Statements

OOP Concepts

Parameterized - Type

Exceptions

Scala Annotation

Methods

String

Scala Packages

Scala Trait

Collections

Scala Options

Miscellaneous Topics

The Factory Pattern in Scala

The Factory Pattern is a creational design pattern that provides an interface for creating objects but allows subclasses to alter the types of objects that will be created. In Scala, thanks to its mix of object-oriented and functional programming paradigms, there are several idiomatic ways to implement the Factory Pattern.

Here's a simple example illustrating the Factory Pattern in Scala:

Problem Statement:

Suppose we want to create different types of shapes. We'll have a Shape trait and different classes implementing this trait (like Circle and Rectangle). We want a ShapeFactory that will produce instances of these shapes based on input.

Implementation:

  • Define the Product Trait and Concrete Products:
trait Shape {
  def draw(): Unit
}

class Circle extends Shape {
  def draw(): Unit = {
    println("Drawing a Circle!")
  }
}

class Rectangle extends Shape {
  def draw(): Unit = {
    println("Drawing a Rectangle!")
  }
}
  • Define the Factory:
object ShapeFactory {
  def getShape(shapeType: String): Shape = shapeType.toLowerCase match {
    case "circle" => new Circle()
    case "rectangle" => new Rectangle()
    case _ => throw new IllegalArgumentException(s"Unknown shape type: $shapeType")
  }
}
  • Use the Factory:
object FactoryDemo extends App {
  val circle: Shape = ShapeFactory.getShape("circle")
  circle.draw()  // Drawing a Circle!

  val rectangle: Shape = ShapeFactory.getShape("rectangle")
  rectangle.draw()  // Drawing a Rectangle!
}

Points to Note:

  • Singleton Factory: We've used Scala's object to create a Singleton Factory. This avoids unnecessary instantiation and provides a global point of access.

  • Pattern Matching: Scala's pattern matching is a concise way to handle different factory creation scenarios.

  • Extensibility: To support a new shape, you would create a new class that extends the Shape trait and then extend the ShapeFactory's pattern match to account for the new shape type.

While the above example provides a simple Factory Pattern, Scala's flexibility offers more advanced factory methods, like using type classes, implicit parameters, or even abstract factories. The best approach would depend on the complexity of the use case and the desired design outcomes.

  1. Factory Pattern implementation in Scala:

    • Description: The Factory Pattern is a creational design pattern that provides an interface for creating objects in a super class but allows subclasses to alter the type of objects that will be created.
    • Code:
      // Product interface
      trait Product
      
      // Concrete products
      class ConcreteProductA extends Product
      class ConcreteProductB extends Product
      
      // Factory interface
      trait Factory {
        def createProduct: Product
      }
      
      // Concrete factories
      class ConcreteFactoryA extends Factory {
        override def createProduct: Product = new ConcreteProductA
      }
      
      class ConcreteFactoryB extends Factory {
        override def createProduct: Product = new ConcreteProductB
      }
      
  2. Creating objects with Factory Pattern in Scala:

    • Description: Objects are created through factory methods, which encapsulate the instantiation logic and allow for flexibility in object creation.
    • Code:
      val factoryA: Factory = new ConcreteFactoryA
      val productA: Product = factoryA.createProduct
      
  3. Scala Factory Method design pattern example:

    • Description: The Factory Method is a specific form of the Factory Pattern where a method is responsible for creating objects in subclasses.
    • Code:
      // Creator trait
      trait Creator {
        def factoryMethod: Product
      }
      
      // Concrete creators
      class ConcreteCreatorA extends Creator {
        override def factoryMethod: Product = new ConcreteProductA
      }
      
      class ConcreteCreatorB extends Creator {
        override def factoryMethod: Product = new ConcreteProductB
      }
      
  4. Abstract Factory Pattern in Scala:

    • Description: The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.
    • Code:
      // Abstract product families
      trait AbstractProductA
      trait AbstractProductB
      
      // Abstract factory
      trait AbstractFactory {
        def createProductA: AbstractProductA
        def createProductB: AbstractProductB
      }
      
      // Concrete product families
      class ConcreteProductA1 extends AbstractProductA
      class ConcreteProductB1 extends AbstractProductB
      
      class ConcreteProductA2 extends AbstractProductA
      class ConcreteProductB2 extends AbstractProductB
      
      // Concrete factories
      class ConcreteFactory1 extends AbstractFactory {
        override def createProductA: AbstractProductA = new ConcreteProductA1
        override def createProductB: AbstractProductB = new ConcreteProductB1
      }
      
      class ConcreteFactory2 extends AbstractFactory {
        override def createProductA: AbstractProductA = new ConcreteProductA2
        override def createProductB: AbstractProductB = new ConcreteProductB2
      }
      
  5. Scala Factory Pattern with case classes:

    • Description: Case classes in Scala can be used in conjunction with Factory Patterns to simplify the creation of immutable data structures with minimal boilerplate.
    • Code:
      // Product case class
      case class Product(name: String, price: Double)
      
      // Factory object
      object ProductFactory {
        def createProduct(name: String, price: Double): Product = Product(name, price)
      }