[dart] learning record of dart constructor (including the writing method of dart singleton mode)

Normal constructor

1. Without specifying the constructor, dart will create a default parameterless constructor for you

2. If you specify a constructor

Similar to Java, dart can use a function with the same name as class as its constructor, such as

class Student {
  int age = 0;
  int id = 0;

  Student(int age, int id) {
    this.age = age;
    this.id = id;
  }
}

This above represents the instance of the current class. For dart, this can be ignored, but in the above example, because the name of the class variable is the same as the name of the parameter passed in by the constructor, this needs to be added to distinguish.

Abbreviation:

class Student {
  int age = 0;
  int id = 0;

  Student(this.age, this.id);
}

Named constructor

Unlike other languages, dart can also use named constructors

Format is

ClassName.identifier

example

class Student {
  int age = 0;
  int id = 0;

  Student(this.age, this.id);
  
  Student.fromJson(Map data) {
  	print("Named constructor Student.fromJson");
  }
}

The named constructor is not inheritable. If a subclass wants to have the same named constructor as the parent class, it should write one with the same name (usually it will call the named constructor with the same name as the parent class in the named constructor of the subclass)

Execution order of constructors

Classes in dart can be inherited, so let's explore the execution order of constructors of subclasses of dart

If you do not specify a constructor for a dart class, dart will automatically generate a parameterless constructor for the class. If the class is a subclass, it will automatically call the parameterless constructor of the parent class.

For the constructor of subclasses, there are three steps to initialize:

1. Call initialization list

2. Call the constructor of the parent class

3. Call your own constructor

In step 2, if the parent class does not have a default parameterless constructor, you need to manually specify the specific parent class constructor. How to call it?

It can be used directly after the constructor of the subclass: the operator is connected with the constructor of the parent class, for example

class Student {
	String? firstName;
	Student.fromJson(Map data) {
		print("in Student");
	}
}

class Jone extends Student {
	Jone.fromJson(Map data) : super.from(data) {
		print("in Jone");
	}
}

After understanding the constructor of the parent class, let's take a look at the initialization list

The initialization list is the code executed before the constructor is executed. Like calling the constructor of the parent class, it also uses: operator, example

Point.fromJson(Map<String,double> json) : x = json['x']! , y = json['y'] {
	print('In Point.fromJson(): ($x, $y)');
}

Initializing the list is very useful, especially when initializing those final decorated member variables, because in the method body, you cannot assign values to final decorated member variables

import 'dart:math';

class Point {
  final num x;
  final num y;
  final num distanceFromOrigin;

  Point(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}

main() {
  var p = new Point(2, 3);
  print(p.distanceFromOrigin);
}
Fields that will be initially assigned in the constructor should not be decorated with late
///Not recommended
class Test{
    late int x;
    Test(int p){
        x = p + 1;
    }
}
///Recommend
class Test{
    int x;
    Test(int p)
    : x = p + 1;
}

Redirect constructor

If a constructor needs to call another constructor without any change, it can use the redirection constructor. The redirection constructor also uses: operator, followed by another constructor, for example

class Point {
	double x,y;
	// primary constructor 
	Point(this.x, this.y);
	
	// Redirect constructor
	Point.alongXAxix(double x) : this(x,0);
}

Constant constructor

If the attributes in the object will not change after creation, you can use the Constant constructor, that is, add the const modifier in front of the constructor, and all initialized attributes should be decorated with final:

class ImmutablePoint {
	static const ImmutalbePoint origin = ImmutablePoint(0,0);
	
	final double x, y;
	const Immutable(this.x, this.y)
}

Factory constructor

By default, the constructor in dart class returns a new instance of this class, but we may choose the returned object in actual application development, such as returning an existing object from the cache or returning a specific implementation subclass of this class.

In order to realize this function, dart has a Factory keyword, and the constructor using Factory is called Factory constructor.

class Student {
	final String name;
	
	static final Map<String, Student> _studentMap = <String, Student>{};
	
	factory Student(String name) {
		return _studentMap.putIfAbsent(name, ()=>Student._newStudent(name));
	}
	
	factory Student.fromJson(Map<String, Object> json) {
		return Student(json['name'].toString())
	}
	
	Student._newStudent(this.name);
}

Note 1. There can only be one unnamed constructor in dart. For the named constructor, the name cannot be repeated, otherwise The default constructor is already defined exception will be reported

Note 2. The factory constructor has no right to access this

In the above code, factory Student is an unnamed constructor, while factory Student.fromjason is a named constructor.

So if you add an unnamed constructor to the Student class, as follows

Student(this.name);

An error will be reported.

So the question is, what is the difference between a factory constructor and a normal constructor?

The biggest difference between them is that the ordinary constructor has no return value, while the factory constructor needs a return value.

factory is used to implement singleton. See the singleton mode below for details

class Singleton {

  static final Singleton _singleton = Singleton.internal();
  factory Singleton() => _singleton;

  Singleton.internal();
}

Factory constructors can be used in combination with named constructors and constant constructors

///Constant constructor
const TestModule(this.x);

///Factory constructor + named constructor
factory TestModule.fromJson(Map map){
    ...ellipsis
    return TestModule(map['x']);
}
private constructors

Use_ Underline for private declaration

class TestModule {
    //private constructors 
    TestModule._origin();
}

In summary, the Dart constructor has four formats:

ClassName(...) // Normal constructor
ClassName.identifier(...) // Named constructor
const ClassName(...) // Constant constructor
factory ClassName(...) // Factory constructor

reference resources:

https://www.jianshu.com/p/24d0927f7625

https://juejin.cn/post/6844903902563794958

https://segmentfault.com/a/1190000040956384

https://dart.cn/guides/language/language-tour#constructors

Singleton mode

Generally speaking, to use the singleton mode in the code, there will be the following conventional requirements in the structure:

  • A Singleton class contains a static attribute instance that references its own class, and it can create this instance by itself.
  • This instance can only be accessed through the static method getInstance().
  • Class constructors usually have no parameters and are marked private, ensuring that the class cannot be instantiated from outside the class.

Following these requirements, it is not difficult for us to write a common singleton pattern with Dart:

class Singleton {
  static Singleton _instance;
  
  // Private named constructor
  Singleton._internal();
  
  static Singleton getInstance() {
    if (_instance == null) {
      _instance = Singleton._internal();
    }
    
    return _instance;
  }
}

At the same time, when implementing the singleton mode, you also need to consider the following points to prevent problems in the use process:

  • Whether lazy loading is required, that is, class instances are created only when needed for the first time.
  • Whether it is thread safe or not, we need to consider the concurrency of multithreading in Java, C++ and other multithreaded languages. Since Dart is the language of single thread model, all codes usually run in the same isolate, so there is no need to consider thread safety.
  • In some cases, the singleton pattern will be considered as an anti pattern, because it violates the single responsibility principle in the SOLID principle. The singleton class controls its own creation and life cycle, and the singleton pattern generally has no interface, so it is difficult to expand.
  • The use of singleton mode will affect the testability of the code. If the singleton class depends on heavy external resources, such as DB, we hope to replace it by mocking when writing unit tests. However, the hard coded use of singleton class makes it impossible to realize mock replacement.

In the actual coding process, the common applications of singleton mode are:

  • The Logger class of the global log, the configuration data object class of the application global, and the single business management class.
  • Classes that consume more resources when creating instances or take a long time to instantiate.
  • Wait

Dart ization

As mentioned above, Dart language is the language of single thread model. When implementing singleton mode, we can no longer consider thread safety. Many other features of Dart can still help us to achieve a more darted singleton.

Using the getter operator, we can break the established rule in the singleton mode that we must write a getInstance() static method, and simplify the template code we must write, as shown in the following get instance:

class Singleton {
  static Singleton _instance;
  static get instance {
    if (_instance == null) {
      _instance = Singleton._internal();
    }
    
    return _instance;
  }
  
  Singleton._internal();
}

Dart's getter is used in the same way as ordinary methods, except that the caller no longer needs to use parentheses, so we can directly use the following methods to get this singleton object when using it:

final singleton = Singleton.instance;

The unique factory constructor in dart also has the feature of not having to create new class instances every time. By using this feature, we can write a more elegant Dart(able) singleton pattern, as follows:

class Singleton {
  static Singleton _instance;
  
  Singleton._internal();
  
  // Factory constructor
  factory Singleton() {
    if (_instance == null) {
      _instance = Singleton._internal();
    }
    
    return _instance;
  }
}

Here we no longer use the getter operator to provide an additional function, but hand over the generation of the singleton object to the factory constructor. At this time, the factory constructor is only created when it is needed for the first time_ Instance, and then return the same instance every time. At this time, we can get the singleton by using the normal constructor as follows:

final singleton = Singleton();

If you also master the features of Dart such as empty security and arrow function, you can use another way to further simplify the code and write Dart flavored code like the following:

class Singleton {
  static Singleton _instance;

  Singleton._internal() {
    _instance = this;
  }

  factory Singleton() => _instance ?? Singleton._internal();
}

Here, use?? As_ The empty judgment operator of instance. If it is empty, call the constructor to instantiate. Otherwise, return directly, which can also achieve the effect of singleton.

Above, none of the lazy loading in the Dart singleton is not realized by using space determination (if (_instance == null) or???), But there is also a very important operator late in Dart's empty security feature, which realizes the lazy loading of instances at the language level, as shown in the following example:

class Singleton {
  Singleton._internal();
  
  factory Singleton() => _instance;
  
  static late final Singleton _instance = Singleton._internal();
}

Variables marked as late_ The initialization of instance will be delayed until the field is first accessed, rather than initialized when the class is loaded. In this way, the implementation of the unique singleton pattern of Dart language has come into being.

Tags: Flutter dart

Posted by NoPHPPhD on Fri, 05 Aug 2022 22:03:36 +0300