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 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:
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.
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!") } }
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") } }
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! }
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.
Factory Pattern implementation in Scala:
// 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 }
Creating objects with Factory Pattern in Scala:
val factoryA: Factory = new ConcreteFactoryA val productA: Product = factoryA.createProduct
Scala Factory Method design pattern example:
// 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 }
Abstract Factory Pattern in Scala:
// 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 }
Scala Factory Pattern with case classes:
// Product case class case class Product(name: String, price: Double) // Factory object object ProductFactory { def createProduct(name: String, price: Double): Product = Product(name, price) }