DEV Community

Aqueel Aboobacker
Aqueel Aboobacker

Posted on • Originally published at aqueelaboobacker.com

This is why you need to use a factory constructor

In Dart, we can write multiple constructors for a class which allows different types of initialization. Writing factory keyword in front of a normal constructor or a named constructor will make it a factory constructor.

class MyClass {
   // ...
   factory MyClass() {
     //..
   }
}
Enter fullscreen mode Exit fullscreen mode

There are three use cases for a factory constructor.

  1. The constructor is expensive, so you want to return an existing instance – if possible – instead of creating a new one.
  2. To implement singleton pattern
  3. You want to return a subclass instance instead of the class itself.

The warehouse

The is the first use case where the constructor is expensive and you want to avoid the expensive work if possible. And this works exactly like a warehouse. Let’s say there is a warehouse for expensive shoes and a customer ordered a pair of shoes. Now the warehouse owner first looks if he has the stock available for that particular product. If it is available then he will deliver the order immediately. Otherwise, he will ask the production team to build new ones. This is exactly how the first use case works.

class Product {

  // private cache
  static final _shelf = <String, Product>{};

  factory Product(String productId) {
     return _shelf.putIfAbsent(
        productId, () => Product._buildNewProduct(productId));
  }

  Product._buildNewProduct(String productId) {
    //..
  }

}
Enter fullscreen mode Exit fullscreen mode

The “underscore” in front of a variable or a method will make it private to the class. Here running _buildNewProduct method is expensive and we decided to avoid calling it every time if possible.

The singleton pattern

In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of class to one single instance.

This is useful when only one instance of that class is needed throughout the application. The most common use case is a local storage database. We need only a single instance of that class throughout the application.

class LocalStorage {

  static final _instance = LocalStorage._internal();

  factory LocalStorage() {
     return _instance;
  }

  LocalStorage._internal();

  Future<void> save(key, val) async {
    //.. 
  }

  Future read(key) async {
    //..
  }
}
Enter fullscreen mode Exit fullscreen mode

Returning a subclass instance

There will be situations where you need to return subclass instance where constructor will act like instance factory which return appropriate subclass based on provided input.

abstract class Shape {
  double area();

  factory Shape.fromType(String type) {
    if (type == 'square') return Square();
    if (type == 'circle') return Circle();
    throw Exception("unknown type");
  }
}

class Square implements Shape {
  double side = 5;

  @override
  double  area() => pow(side, 2);

}

class Circle implements Shape {
  double radius = 5;

  @override
  double  area() => pi * pow(radius, 2);
}
Enter fullscreen mode Exit fullscreen mode

You can use the abstract base class for returning subclass instances based on how the factory constructer is called. This is a simplified way to using the popular factory design pattern.

var shape = Shape.fromType("square");
print(shape.area());
Enter fullscreen mode Exit fullscreen mode

At first glance the factory keyword will be confusing, and you will limit yourself into a single use case or even avoiding it completely. But when you look deeper and understand the concepts you will fall in love with this. This is an important feature of Dart programming language.

Top comments (0)