了解工厂构造函数代码示例-Dart

关于这里提到的工厂构造函数示例(https://www.dartlang.org/guides/language/language-tour#factory-constructors) ,我有一些琐碎的问题。 我知道在基本层次上只有三种类型的构造函数——默认构造函数、命名构造函数和参数构造函数。

  1. 为什么我要在这个例子中使用 factory呢?
  2. 这是正在使用的命名构造函数吗? 为什么? Example factory constructor
104645 次浏览
  1. 静态方法和工厂构造函数之间没有太大区别。

    对于工厂构造函数,返回类型固定为类的类型,而对于静态方法,可以提供自己的返回类型。

    可以使用 new调用工厂构造函数,但是在 Dart 2中,使用可选的 new调用工厂构造函数几乎是不相关的。

    还有其他一些特性,比如重定向,它们很少被(工厂)构造函数所支持,但是静态方法却不支持。

    使用工厂构造函数来创建类的实例,而不是使用静态方法来使对象创建的目的更加明显,这可能仍然是一个好的实践。

    这就是在您发布的示例中使用工厂构造函数的原因,也许是因为代码最初是在 Dart 1中编写的,它允许使用 new创建日志记录器实例,就像使用其他类一样。

  2. 是的,这是一个命名构造函数,前缀 _使它成为一个私有的命名构造函数。只有指定的构造函数可以变为私有的,否则就没有地方添加 _前缀。

    它用于防止从公共工厂构造函数以外的任何地方创建实例。这样可以确保应用程序中不能有多个 Logger实例。 工厂构造函数只在第一次创建实例,对于后续调用,始终返回以前创建的实例。

必须的不想返回类本身的 新的实例的情况下使用工厂。用例:

  • 构造函数开销很大,因此您希望返回一个现有的实例(如果可能的话) ,而不是创建一个新的实例;
  • 你只想创建一个类的一个实例(单例模式) ;
  • 你想要返回一个子类实例而不是类本身。

解释

Dart 类可能有 生成构造函数生成构造函数工厂建筑工人。生成构造函数是一个总是返回类的新实例的函数。因此,它不使用 return关键字。一个常见的生成构造函数的形式是:

class Person {
String name;
String country;


// unnamed generative constructor
Person(this.name, this.country);
}
var p = Person("...") // returns a new instance of the Person class

工厂构造函数比生成构造函数具有更松散的约束。工厂只需返回与类相同类型的实例或实现其方法的实例(即满足其接口)。这个 可以是该类的一个新实例,但也可以是该类的一个现有实例或子类的一个新的/现有实例(它必须具有与父类相同的方法)。工厂可以使用控制流来确定要返回的对象,并且必须使用 return关键字。为了让工厂返回一个新的类实例,它必须首先调用一个生成构造函数。

Dart Factory Explained

在您的示例中,未命名的工厂构造函数首先读取名为 _cache的 Map 属性(因为它是 Static,所以它存储在类级别,因此独立于任何实例变量)。如果一个实例变量已经存在,它将被返回。否则,通过调用命名的生成构造函数 Logger._internal生成一个新实例。缓存此值,然后返回。因为生成构造函数只接受一个 name参数,所以 mute属性将始终初始化为 false,但是可以使用默认 setter 进行更改:

var log = Logger("...");
log.mute = true;
log.log(...); // will not print

factory这个术语暗指工厂模式,它是关于允许构造函数根据提供的参数返回子类实例(而不是类实例)。Dart 中这个用例的一个很好的例子是抽象的 HTML 元素类,它定义了几十个命名的工厂构造函数,返回不同的子类。例如,Element.div()Element.li()分别返回 <div><li>元素。

在这个缓存应用程序中,我发现“工厂”有点用词不当,因为它的目的是避免对生成构造函数的调用,而且我认为现实世界中的工厂本质上是生成的。也许这里更合适的术语是“仓库”: 如果一个物品已经可用,把它从架子上拿下来并交付。如果没有,那就换个新的。

所有这些与命名构造函数有什么关系? 生成构造函数和工厂构造函数都可以是未命名的或命名的:

...
// named generative
// delegates to the default generative constructor
Person.greek(String name) : this(name, "Greece");


// named factory
factory Person.greek(String name) {
return Greek(name);
}
}


class Greek extends Person {
Greek(String name) : super(name, "Greece");
}




作为对 Dave 答案的补充,这段代码显示了一个清晰的示例,当使用 Factory 返回与父类相关的类时。

看看 https://codelabs.developers.google.com/codelabs/from-java-to-dart/#3的这个代码

你可以在这里运行这个代码

做一些修改,以便了解它在某些情况下是如何工作的,比如单例模式。

import 'dart:math';


abstract class Shape {
factory Shape(String type) {
if (type == 'circle') return Circle(2);
if (type == 'square') return Square(2);
// To trigger exception, don't implement a check for 'triangle'.
throw 'Can\'t create $type.';
}
num get area;
}


class Circle implements Shape {
final num radius;
Circle(this.radius);
num get area => pi * pow(radius, 2);
}


class Square implements Shape {
final num side;
Square(this.side);
num get area => pow(side, 2);
}


class Triangle implements Shape {
final num side;
Triangle(this.side);
num get area => pow(side, 2) / 2;
}


main() {
try {
print(Shape('circle').area);
print(Shape('square').area);
print(Shape('triangle').area);
} catch (err) {
print(err);
}
}

除了其他答案之外,还要考虑 实例化对象的顺序和创建实例的时间:

正规构造函数正规构造函数中,创建一个实例,并使用初始化器列表实例化最终变量。这就是为什么没有 return语句。在执行构造函数时,使用 要返回的实例已经修复

工厂建造者工厂建造者中,返回的实例由方法决定。这就是为什么它需要一个 return语句,以及为什么它通常至少在一个路径中调用一个普通的构造函数。

因此,工厂所做的与静态方法所做的没有什么不同(在其他语言中通常称为 getInstance()) ,除了不能用静态方法重载构造函数,但可以用工厂方法重载构造函数。也就是说,工厂方法是一种隐藏类的用户调用的不是构造函数而是静态方法这一事实的方法:

// C++
MyCoolService.getInstance()


// Dart
MyCoolService()