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() {
//..
}
}
There are three use cases for a factory constructor.
- The constructor is expensive, so you want to return an existing instance – if possible – instead of creating a new one.
- To implement singleton pattern
- 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) {
//..
}
}
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 {
//..
}
}
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);
}
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());
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)