简体   繁体   English

dart 工厂构造器标识符的优点

[英]dart advantage of a factory constructor identifier

I've been investigating JSON parsing for my Flutter app and have a question about factory constructors that I can't resolve.我一直在为我的 Flutter 应用程序调查 JSON 解析,并且对我无法解决的工厂构造函数有疑问。 I'm trying to understand the advantage of using a factory constructor versus a plain constructor.我试图了解使用工厂构造函数与普通构造函数的优势。 For example, I see quite a few JSON parsing examples that create a model class with a JSON constructor like this:例如,我看到很多 JSON 解析示例创建 model class 和 Z0ECD11C41D7A287A201

class Student{
  String studentId;
  String studentName;
  int studentScores;

  Student({
    this.studentId,
    this.studentName,
    this.studentScores
  });

  factory Student.fromJson(Map<String, dynamic> parsedJson){
    return Student(
      studentId: parsedJson['id'],
      studentName : parsedJson['name'],
      studentScores : parsedJson ['score']
    );
  }
}

I've also seen an equal number of examples that DON'T declare the constructor as a factory.我还看到了相同数量的不将构造函数声明为工厂的示例。 Both types of classname.fromJSON constructors create an object from the JSON data so is there an advantage to declaring the constructor as a factory or is using a factory here superfluous?两种类型的 classname.fromJSON 构造函数都从 JSON 数据创建了 object ,因此将构造函数声明为工厂或在此处使用工厂是多余的吗?

A normal constructor always returns a new instance of the current class (except when the constructor throws an exception).普通构造函数总是返回当前类的一个新实例(除非构造函数抛出异常)。

A factory constructor is quite similar to a static method with the differences that it工厂构造函数与静态方法非常相似,不同之处在于它

  • can only return an instance of the current class or one of its subclasses只能返回当前类或其子类之一的实例
  • can be invoked with new but that is now less relevant since new became optional.可以用new调用,但现在不太相关,因为new成为可选的。
  • has no initializer list (no : super() )没有初始化列表(没有: super()

So a factory constructor can be used所以可以使用工厂构造函数

  • to create instances of subclasses (for example depending on the passed parameter创建子类的实例(例如取决于传递的参数
  • to return a cached instance instead of a new one返回缓存实例而不是新实例
  • to prepare calculated values to forward them as parameters to a normal constructor so that final fields can be initialized with them.准备计算值以将它们作为参数转发给普通构造函数,以便可以使用它们初始化最终字段。 This is often used to work around limitations of what can be done in an initializer list of a normal constructor (like error handling).这通常用于解决在普通构造函数的初始化列表中可以完成的操作的限制(如错误处理)。

In your example this code在您的示例中,此代码

  studentId: parsedJson['id'],
  studentName : parsedJson['name'],
  studentScores : parsedJson ['score']

could be moved to the body of a normal constructor because no final fields need to be initialized.可以移动到普通构造函数的主体,因为不需要初始化final字段。

A factory constructor vs. a normal constructor工厂构造函数与普通构造函数

  • A factory constructor invokes another constructor.工厂构造函数调用另一个构造函数。
  • Since a factory constructor does not directly create a new instance, it cannot use a constructor initializer list.由于工厂构造函数不直接创建新实例,因此它不能使用构造函数初始值设定项列表。
  • A normal constructor always returns a new instance of the class.一个普通的构造函数总是返回一个类的新实例。 A factory constructor is permitted to return an existing instance, an instance of a derived class, or null .允许工厂构造函数返回现有实例、派生类的实例或null (However, some people dislike returning null from a factory constructor . Note that returning null from a factory constructor is disallowed with null-safety.) (然而, 有些人不喜欢返回null从工厂构造函数。需要注意的是返回null从一个工厂的构造是不允许用空安全。)
  • Due to the above, an extending class cannot invoke a factory constructor as the superclass constructor.由于上述原因,扩展类不能调用工厂构造函数作为超类构造函数。 A class that provides only factory constructors therefore cannot be extended with derived classes.因此,仅提供factory构造函数的类不能使用派生类进行扩展。

A factory constructor vs. a static method工厂构造函数与静态方法

  • A factory constructor can be the unnamed, default constructor of a class.工厂构造函数可以是类的未命名的默认构造函数。
  • A factory constructor can be used with new .工厂构造函数可以与new一起使用。 (But using new is now discouraged .) (但现在不鼓励使用new 。)
  • Static methods can be used to create tear-offs (ie, they can be used as callbacks) but constructors currently can't .静态方法可用于创建撕裂(即,它们可用作回调)但构造函数目前不能
  • Static methods can be async .静态方法可以是async (A factory constructor must return a type of its class, so it cannot return a Future .) (工厂构造函数必须返回其类的类型,因此它不能返回Future 。)
  • Factory constructors can be declared const .工厂构造函数可以声明为const
  • In null-safe Dart, a factory constructor cannot return a nullable type.在空安全 Dart 中,工厂构造函数不能返回可空类型。
  • In generated dartdoc documentation, a factory constructor obviously will be listed in the "Constructors" section (which is prominently at the top) whereas a static method will be in the "Static Methods" section (which currently is buried at the bottom).在生成的 dartdoc 文档中,工厂构造函数显然将列在“构造函数”部分(位于顶部的显眼位置)中,而静态方法将在“静态方法”部分(当前隐藏在底部)中。

After I've been noticing and wondering the same, and given I don't think the other answers actually answer the question ("I've been investigating JSON parsing [...] I'm trying to understand the advantage of using a factory constructor verses a plain constructor"), here my try:在我注意到并想知道同样的问题之后,鉴于我认为其他答案实际上并没有回答这个问题(“我一直在研究 JSON 解析 [...] 我试图了解使用工厂构造函数与普通构造函数相对”),这里是我的尝试:

there's no advantage or difference that I could see or understand, when parsing json, in using a factory constructor instead of a plain constructor.在解析 json 时,使用工厂构造函数而不是普通构造函数没有任何我能看到或理解的优势或差异。 I tried both, and both works fine, with all the types of parameters.我对所有类型的参数都试过,两者都可以正常工作。 I decided eventually to adopt the factory constructor, because of the convenience of how the code is written, and readability, but it's a matter of choice and both will work fine in all the cases.我最终决定采用工厂构造函数,因为代码编写方式方便且可读性强,但这是一个选择问题,两者在所有情况下都可以正常工作。

One of the uses of factory constructor is, we can decide which instance to create, at run-time and move all the logic to the parent's factory constructor工厂构造函数的用途之一是,我们可以决定在运行时创建哪个实例并将所有逻辑移动到父级的工厂构造函数

let's say you have 1 parent class and 2 subclasses假设您有 1 个父类和 2 个子类

class GolderRetriever extends Dog{
   GolderRetriever(String name):super(name);
}
class Labrador extends Dog{
  Labrador(String name):super(name);
}

Then we have the parent class然后我们有父类

class Dog{
  final String name;
  Dog(this.name);
  
  
  factory Dog.createInstance({required String name,DogType type=DogType.UNKNOWN}){
    
    if(type==DogType.GOLDEN_RETRIEVER){
      return GolderRetriever(name);
    }
    else if(type==DogType.DALMATION){
      return Labrador(name);
    }else{
    return Dog(name);
    }
  }
}

and also I have enum DogType而且我有 enum DogType

enum DogType{
  GOLDEN_RETRIEVER,DALMATION,UNKNOWN
}

Then in the main Method, you just delegate which subclass instance you want to create to the parent Dog class然后在主方法中,您只需将要创建的子类实例委托给父 Dog 类

main() {
     
  Dog myDog = Dog.createInstance(name:"Rocky",type:DogType.DALMATION);
  Dog myNeighboursDog =  Dog.createInstance(name:"Tommy",type:DogType.GOLDEN_RETRIEVER);
  Dog strayDog = Dog.createInstance(name:"jimmy");
}

you can't do this with a named constructor as you can create only the instance of that class(Dog class), not its subtypes.您不能使用命名构造函数执行此操作,因为您只能创建该类(Dog 类)的实例,而不是其子类型。

Now the responsibility of which instance to create is delegated to the parent class.现在,创建哪个实例的责任委托给父类。 This can remove a lot of if-else boilerplate code.这可以删除很多 if-else 样板代码。 When you want to change the logic, you just change that in Animal class alone.当您想更改逻辑时,只需在 Animal 类中更改即可。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM