【问题标题】:dart advantage of a factory constructor identifier工厂构造函数标识符的飞镖优势
【发布时间】:2019-02-17 08:07:38
【问题描述】:

我一直在调查我的 Flutter 应用程序的 JSON 解析,并且有一个关于我无法解决的工厂构造函数的问题。我试图了解使用工厂构造函数与普通构造函数的优势。例如,我看到很多 JSON 解析示例,它们使用如下 JSON 构造函数创建模型类:

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']
    );
  }
}

我还看到了相同数量的不将构造函数声明为工厂的示例。两种类型的 classname.fromJSON 构造函数都从 JSON 数据创建一个对象,因此将构造函数声明为工厂或在这里使用工厂是多余的吗?

【问题讨论】:

  • 工厂构造函数不是真正的构造函数。它们是隐藏在假构造函数后面的函数。它们用于替换您在其他语言中使用的静态方法
  • 这帮助我理解了它:stackoverflow.com/a/56107639

标签: dart flutter


【解决方案1】:

普通构造函数总是返回当前类的新实例(构造函数抛出异常时除外)。

工厂构造函数与静态方法非常相似,不同之处在于它

  • 只能返回当前类或其子类之一的实例
  • 可以使用 new 调用,但现在不再相关,因为 new 变为可选。
  • 没有初始化列表(没有: super()

所以可以使用工厂构造函数

  • 创建子类的实例(例如取决于传递的参数
  • 返回一个缓存实例而不是一个新实例
  • 准备计算值以将它们作为参数转发给普通构造函数,以便可以使用它们初始化最终字段。这通常用于解决在普通构造函数的初始化列表中可以完成的操作的限制(如错误处理)。

在您的示例中,此代码

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

可以移动到普通构造函数的主体,因为不需要初始化 final 字段。

【讨论】:

  • 我认为工厂构造函数的主要目的是隐藏它作为实现细节的静态函数的事实;提供实例化对象的一致方式
  • 您不能像使用回调一样将其用作变量。但是,是的,飞镖 2 的收益是有限的
  • 可能提到子类不能super.factory
  • 感谢您的回复,冈特。似乎使用工厂构造函数与命名构造函数具有有效的特殊情况用途,但不应随意使用。我看到的大多数示例可以仅使用命名构造函数就可以充分解决,但使用的是工厂构造函数。在我在问题中使用的示例中,正确的实现似乎是这样的:Student.fromJson(Map&lt;String, dynamic&gt; parsedJson) : studentId = parsedJson['id'], studentName = parsedJson['name'], studentScores = parsedJson['score'];
  • 对,如果你在 final 类中创建了 3 个字段,它看起来会有所不同。
【解决方案2】:

在问题的特定示例中,使用 factory 构造函数没有任何优势。它对调用者没有任何影响(不期望接收一个已经存在的对象),并且这个特定的 factory 构造函数可能是一个普通的构造函数,它被委托给了主构造函数。

一般来说,factory 关键字不是很有用,只有在特殊情况下才有优势。


工厂构造函数与普通构造函数

  • 工厂构造函数调用另一个构造函数。
  • 由于工厂构造函数不直接创建新实例,因此不能使用构造函数初始化列表。
  • 普通的构造函数总是返回一个新的类实例。允许工厂构造函数返回现有实例、派生类的实例或null。 (但是,some people dislike returning null from a factory constructor。请注意,从工厂构造函数返回 null 是不允许使用 null 安全的。)
  • 由于上述原因,扩展类不能调用工厂构造函数作为超类构造函数。因此,不能使用派生类扩展仅提供 factory 构造函数的类。

工厂构造函数与静态方法

  • 工厂构造函数可以是类的未命名的默认构造函数。
  • 工厂构造函数可以与new 一起使用。 (但是using new is now discouraged。)
  • 在 Dart 2.15 之前,constructors could not be used as tear-offs(即它们可以用作回调),而 static 方法可以。
  • 静态方法可以是async。 (工厂构造函数必须返回其类的类型,因此它不能返回 Future。)
  • 工厂构造函数可以声明为const
  • 在 null 安全 Dart 中,工厂构造函数不能返回可为 null 的类型。
  • 在生成的 dartdoc 文档中,工厂构造函数显然会列在“构造函数”部分(位于顶部显着位置)中,而静态方法将列在“静态方法”部分(目前隐藏在底部) )。

【讨论】:

  • 这并没有真正回答这个问题。您分别解释了工厂构造函数和普通构造函数以及静态方法之间的区别。相反,问题是关于为什么在解析 json 时使用工厂构造函数,在官方示例中给出的总是在工厂构造函数中创建一个新实例。
  • @Alessio 这是一个写得很好的答案,他在 Dart 为 Google 工作,我 +1。
  • @iKeepChangingName 没关系,但我仍然认为答案实际上并没有回答问题,同时对工厂构造函数是什么和不是什么做了一个很好的总结。他在 Dart 为 Google 工作的事实也没有改变我的看法。不用担心我们会不同意:)
  • 不喜欢这里的答案。它们过于复杂,无法清楚地回答主要问题。在这里找到了对特定问题的很好解释stackoverflow.com/a/63628949/12695188
【解决方案3】:

在我注意到并想知道同样的事情之后,鉴于我认为其他答案实际上并没有回答这个问题(“我一直在研究 JSON 解析 [...]使用工厂构造函数与普通构造函数的区别”),这是我的尝试:

在解析 json 时,我没有看到或理解使用工厂构造函数而不是普通构造函数的优势或区别。我尝试了两者,并且都可以正常使用所有类型的参数。我最终决定采用工厂构造函数,因为代码编写方式的便利性和可读性,但这是一个选择问题,两者在所有情况下都可以正常工作。

【讨论】:

    【解决方案4】:

    工厂构造函数的一个用途是,我们可以在运行时决定创建哪个实例,并将所有逻辑移至父级的工厂构造函数

    假设你有 1 个父类和 2 个子类

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

    那么我们就有了父类

    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);
        }
      }
    }
    

    我还有枚举 DogType

    enum DogType{
      GOLDEN_RETRIEVER,DALMATION,UNKNOWN
    }
    

    然后在主方法中,你只需将要创建的子类实例委托给父 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");
    }
    

    您不能使用命名构造函数来执行此操作,因为您只能创建该类(Dog 类)的实例,而不是其子类型。

    现在将创建哪个实例的责任委托给父类。这可以删除很多 if-else 样板代码。当您想更改逻辑时,只需在 Animal 类中更改即可。

    【讨论】:

    • 首先,我认为这不能回答问题。其次,我认为父类知道它的子类不是一个好习惯。如果 Dog 将被称为 DogFactory 并且会有一个额外的 DogInterface,那么您的示例会更合适。 DogFactory 然后可以使用内置在 Dart 中的工厂构造函数。
    • 父类不应该知道它的子类,谢谢,这很有道理!!!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-20
    • 2018-10-29
    • 1970-01-01
    • 2020-04-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多