/ Yang Jiakang, CFUG community member, author of "Flutter Development Journey from South to North", Xiaomi engineer
In topics around design patterns, the word factory comes up frequently, from the simple factory pattern to the factory method pattern to the abstract factory pattern . The meaning of the name of the factory is the industrial place where the product is manufactured. It is applied in the object-oriented, and it has become a relatively typical creation mode.
Formally, a factory can be a method/function that returns the object we want, i.e. an abstraction that can act as a constructor.
In this article, I will take you to use Dart to understand their respective implementations and the relationship between them.
Simple Factory & factory keyword
Simple Factory Pattern not among the 23 GoF design patterns, but it is one of the most commonly used programming methods.
It mainly involves a special method, which is specially used to provide the instance object we want (object factory),
We can put this method into a separate class SimpleFactory
as follows:
class SimpleFactory {
/// 工厂方法
static Product createProduct(int type) {
if (type == 1) {
return ConcreteProduct1();
}
if (type == 2) {
return ConcreteProduct2();
}
return ConcreteProduct();
}
}
We believe that the objects to be created by this method belong to the same Product class (abstract class), and specify the specific object type to be created through the parameter type.
Dart does not support interface
keyword, but we can use abstract
to define interfaces as abstract classes,
Then each specific type inherits and implements it:
/// 抽象类
abstract class Product {
String? name;
}
/// 实现类
class ConcreteProduct implements Product {
@override
String? name = 'ConcreteProduct';
}
/// 实现类1
class ConcreteProduct1 implements Product {
@override
String? name = 'ConcreteProduct1';
}
/// 实现类2
class ConcreteProduct2 implements Product {
@override
String? name = 'ConcreteProduct2';
}
When we want to get the corresponding type object in the code, we only need to pass in the desired type value through this method,
We don't have to care about the specific logic of how production is produced and which object is selected:
void main() {
final Product product = SimpleFactory.createProduct(1);
print(product.name); // ConcreteProduct1
}
This is Simple Factory Pattern .
Having said that, I have to mention the unique factory keyword in Dart.
factory The keyword can be used to modify the constructor of the Dart class, meaning the factory constructor , which enables the constructor of the class to naturally have the function of a factory. The usage is as follows:
class Product {
/// 工厂构造函数(修饰 create 构造函数)
factory Product.createFactory(int type) {
if (type == 1) {
return Product.product1;
} else if (type == 2) {
return Product._concrete2();
}
return Product._concrete();
}
/// 命名构造函数
Product._concrete() : name = 'concrete';
/// 命名构造函数1
Product._concrete1() : name = 'concrete1';
/// 命名构造函数2
Product._concrete2() : name = 'concrete2';
String name;
}
factory decorated constructor needs to return an object instance of the current class,
We can call the corresponding constructor according to the parameters and return the corresponding object instance.
void main() {
Product product = Product.createFactory(1);
print(product.name); // concrete1
}
In addition, the factory constructor does not require us to generate new objects every time,
We can also predefine some objects in the class for the factory constructor to use,
In this way, every time an object is constructed with the same parameters, the same object will be returned,
In the chapter of singleton pattern we have already introduced:
class Product {
/// 工厂构造函数
factory Product.create(int type) {
if (type == 1) {
return product1;
} else if (type == 2) {
return product2();
}
return Product._concrete();
}
static final Product product1 = Product._concrete1();
static final Product product2 = Product._concrete2();
}
factory In addition to modifying named constructors, it can also modify default non-named constructors.
class Product {
factory Product(int type) {
return Product._concrete();
}
String? name;
}
So far, one disadvantage of factory constructors has been highlighted, that is, users cannot intuitively feel that they are using factory functions.
The usage of the factory constructor is no different from the ordinary constructor, but the instance produced by this constructor is equivalent to a singleton:
void main() {
Product product = Product(1);
print(product.name); // concrete1
}
Such usage is easy to cause confusion to users, therefore, we should try to use specific
named constructor as a factory constructor (like createFactory
in the example above).
Factory Method Pattern
The factory method pattern is also one of the most commonly used methods in our programming.
In a simple factory, the main service object is the client, and the user of the factory method is not related to the class of the factory itself.
The factory method pattern mainly serves its own parent class, as follows ProductFactory
(analogous to the Creator in UML):
/// 抽象工厂
abstract class ProductFactory {
/// 抽象工厂方法
Product factoryMethod();
/// 业务代码
void dosomthing() {
Product product = factoryMethod();
print(product.name);
}
}
In class ProductFactory
, factory method factoryMethod
is abstract method,
Each subclass must override this method and return a different Product
object,
When the dosomthing()
method is called, different responses can be made according to the returned object.
The specific usage is as follows:
/// 具体工厂
class ProductFactory1 extends ProductFactory {
/// 具体工厂方法1
@override
Product factoryMethod() {
return ConcreteProduct1();
}
}
class ProductFactory2 extends ProductFactory {
/// 具体工厂方法2
@override
Product factoryMethod() {
return ConcreteProduct2();
}
}
/// 使用
main() {
ProductFactory product = ProductFactory1();
product.dosomthing(); // ConcreteProduct1
}
In Flutter, abstract methods have a very practical use case. When we use Flutter to develop multi-terminal applications, we usually need to consider multi-platform adaptation, that is, in multiple platforms, the same operation sometimes produces different results/styles. We can put the logic generated by these different results/styles in in the factory method.
As follows, we define a DialogFacory
as a factory for generating different styles of Dialog:
abstract class DialogFacory {
Widget createDialog(BuildContext context);
Future<void> show(BuildContext context) async {
final dialog = createDialog(context);
return showDialog<void>(
context: context,
builder: (_) {
return dialog;
},
);
}
}
Then, for both Android and iOS platforms, you can create two different styles of Dialog:
/// Android 平台
class AndroidAlertDialog extends DialogFactory {
@override
Widget createDialog(BuildContext context) {
return AlertDialog(
title: Text(getTitle()),
content: const Text('This is the material-style alert dialog!'),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Close'),
),
],
);
}
}
/// iOS 平台
class IOSAlertDialog extends DialogFactory {
@override
Widget createDialog(BuildContext context) {
return CupertinoAlertDialog(
title: Text(getTitle()),
content: const Text('This is the cupertino-style alert dialog!'),
actions: <Widget>[
CupertinoButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Close'),
),
],
);
}
}
Now, we can use the corresponding Dialog like this:
Future _showCustomDialog(BuildContext context) async {
final dialog = AndroidAlertDialog();
// final dialog = IOSAlertDialog();
await selectedDialog.show(context);
}
abstract factory
Compared with simple factory and factory method , the biggest difference between abstract factory pattern is that these two patterns only produce one kind of object, while abstract factory produces a series of objects (object family), and the generated There must be some connection between this series of objects. For example, Apple will produce mobile phone , tablet and many other products, all of which belong to the Apple brand.
Such as the following abstract factory class:
abstract class ElectronicProductFactory {
Product createComputer();
Product createMobile();
Product createPad();
// ...
}
For Apple, I am the factory that produces this type of electronic product, so I can inherit this class and implement the methods in it to produce various products:
class Apple extends ElectronicProductFactory {
@override
Product createComputer() {
return Mac();
}
@override
Product createMobile() {
return IPhone();
}
@override
Product createPad() {
return IPad();
}
// ...
}
Similarly, the same way can be used for electronic product manufacturers such as Huawei and Xiaomi, which is the abstract factory pattern.
In developing Flutter applications, we can also make full use of the abstract factory pattern to adapt to the application. We can define the following abstract factory for producing widgets:
abstract class IWidgetsFactory {
Widget createButton(BuildContext context);
Widget createDialog(BuildContext context);
// ...
}
Our applications usually need to display different styles of widgets for each platform. Therefore, for each platform, we can implement the corresponding implementation factory, as follows:
/// Material 风格组件工厂
class MaterialWidgetsFactory extends IWidgetsFactory {
@override
Widget createButton(
BuildContext context, VoidCallback? onPressed, String text) {
return ElevatedButton(
child: Text(text),
onPressed: onPressed,
);
}
@override
Widget createDialog(BuildContext context, String title, String content) {
return AlertDialog(title: Text(title), content: Text(content));
}
/// ...
}
/// Cupertino 风格组件工厂
class CupertinoWidgetsFactory extends IWidgetsFactory {
@override
Widget createButton(
BuildContext context,
VoidCallback? onPressed,
String text,
) {
return CupertinoButton(
child: Text(text),
onPressed: onPressed,
);
}
@override
Widget createDialog(BuildContext context, String title, String content) {
return CupertinoAlertDialog(
title: Text(title),
content: Text(content),
);
}
// ...
}
In this way, we use MaterialWidgetsFactory
on the Android platform and CupertinoWidgetsFactory
on the iOS platform, and we can use the widgets of the corresponding platforms. To adapt to more platforms, we only need to inherit IWidgetsFactory
to implement the factory class of the corresponding platform.
So far, we can find that as a creational pattern, the main job of these three types of factory patterns is to create objects in different ways, but they each have their own characteristics: the simple factory pattern abstracts produce objects , and the factory method pattern abstracts The class method is , and the abstraction of the factory method pattern is the factory that produces objects How to use it is a matter of opinion.
Extended reading
- Baidu Encyclopedia: Factory Method Pattern
- Baidu Encyclopedia: Abstract Factory Pattern
- Mangirdas Kazlauskas:Flutter Design Patterns: Abstract Factory
About this series of articles
The series of Flutter / Dart Design Patterns from South to North (Flutter Design Patterns for short) is expected to be released in two weeks, focusing on introducing common design patterns and development methods in Flutter application development to developers, aiming to promote the development of Flutter / Dart language features. Popularize, and help developers develop high-quality, maintainable Flutter applications more efficiently.
I am happy to continue to improve the articles in this series. If you have any questions or suggestions about this article, please submit an issue to the official Github repository of the Chinese community or contact me directly, and I will reply in time.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。