为什么在从地图结构构造新实例时使用工厂?

Dan*_*iel 7 dart

我试图真正了解来自 API 的 JSON 数据流,直到我可以将其作为对象使用。

现在我想我已经准备好了一切,我唯一的问题是为什么我看到有些人在使用命名构造函数 .fromJson 时使用工厂。根据 Dart 文档:

当我们实现一个并不总是创建类的新实例的构造函数时,我们使用 factory 关键字。

但是在这种情况下,我们总是会在使用 fromJson 时创建一个实例,对吗?

那么为什么有些人使用:

   factory User.fromJson(Map<String, dynamic> json) =>
      User(name: json['name'], alias: json['alias']);
Run Code Online (Sandbox Code Playgroud)

而不是更合理的:

  User.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        alias = json['alias'];
Run Code Online (Sandbox Code Playgroud)

非常感谢您的任何声明。

lrn*_*lrn 9

使公共构造函数成为工厂可能是一种防御性设计,即使在技术上不需要。

如果你生成一个构造函数,那么某个地方的某个人可能会用一个子类扩展你的类,并将他们的构造函数转发给你的生成构造函数。

到那时,将您的构造函数变成工厂就变成了一个突破性的变化。如果您决定需要更多验证并且如果构造函数是工厂会变得更方便,那么您现在无法进行更改。

生成一个公共构造函数是一个承诺(制作它也是如此const),所以除非你真的想要,否则最好不要这样做。不要偶然或仅仅因为可以公开生成(或 const)构造函数。如果您打算通过该构造函数将该类用作超类,请执行此操作。否则,只公开工厂构造函数并保持生成构造函数私有会更安全。


Sur*_*gch 6

让我在 lrn 的有用解释中添加一些示例。

假设你创建了一个User这样的类:

class User {
  User({this.name, this.alias});

  User.fromJson(Map<String, dynamic> json) {
    return User(name: json['name'], alias: json['alias']);
  }

  final String name;
  final String alias;
}
Run Code Online (Sandbox Code Playgroud)

User.fromJson构造是生成构造函数。生成构造函数的一个特点是你可以从另一个构造函数转发给它们。这意味着其他人可以像这样子类化你的类:

class SoftwareUser extends User {
  SoftwareUser(Map<String, dynamic> json) : super.fromJson(json);
}
Run Code Online (Sandbox Code Playgroud)

super.fromJson(json)部分只是将参数转发给您的原始构造函数。到现在为止还挺好。

但后来您决定在尝试从中创建对象之前,要对该 JSON 映射进行一些错误检查和文本操作。你可以用断言做一些事情,但你是有限的。另一方面,工厂构造函数可以让你做更多的事情。这是在构造函数中做更多工作的示例:

class User {
  User({this.name, this.alias});

  factory User.fromJson(Map<String, dynamic> json) {
    String userName = json['name'];
    String userAlias = json['alias'];
    if (userName == null || userAlias == null) throw FormatException();
    userName = userName.toUpperCase();
    userAlias = userAlias.toUpperCase();
    return User(name: userName, alias: userAlias);
  }

  final String name;
  final String alias;
}
Run Code Online (Sandbox Code Playgroud)

这对User类来说很好,但问题是现在SoftwareUser子类被破坏了。

class SoftwareUser extends User {
  SoftwareUser(Map<String, dynamic> json) : super.fromJson(json); //   <-- error
}

// The constructor 'User User.fromJson(Map<String, dynamic> json)' 
// is a factory constructor, but must be a generative constructor 
// to be a valid superinitializer.
// 
// Try calling a different constructor of the superclass, or making 
// the called constructor not be a factory constructor.
Run Code Online (Sandbox Code Playgroud)

正如错误消息所说,您无法转发到工厂构造函数。

如果您一开始就使用工厂构造函数User,那么SoftwareUser子类将永远无法像那样转发。工厂构造函数将允许您从此更改:

factory User.fromJson(Map<String, dynamic> json) =>
      User(name: json['name'], alias: json['alias']);
Run Code Online (Sandbox Code Playgroud)

对此:

factory User.fromJson(Map<String, dynamic> json) {
  String userName = json['name'];
  String userAlias = json['alias'];
  if (userName == null || userAlias == null) throw FormatException();
  userName = userName.toUpperCase();
  userAlias = userAlias.toUpperCase();
  return User(name: userName, alias: userAlias);
}
Run Code Online (Sandbox Code Playgroud)

不破坏任何子类。这就是 lrn 将其称为防御性设计的原因。即使您不知道您未来的需求,您也可以在不破坏外部依赖关系的情况下对稍后创建对象的方式进行内部更改。