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

时间:2018-12-21 14:19:20

标签: constructor dart

我对这里提到的工厂构造函数示例(https://www.dartlang.org/guides/language/language-tour#factory-constructors)有一些疑问。 我知道在基本级别上只有三种类型的构造函数-默认,命名和参数化。

  1. 为什么在这个示例中我应该完全使用factory
  2. 是否正在使用一个命名构造函数?为什么呢? Example factory constructor

5 个答案:

答案 0 :(得分:3)

1)静态方法和工厂构造函数之间没有太大区别。

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

可以使用new调用工厂构造函数,但是在Dart 2中,它与可选new无关。

(工厂)构造函数支持其他一些功能,如重定向,但很少使用,但静态方法不支持。

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

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

2)是的,这是一个命名构造函数,前缀_使其成为私有的命名构造函数。只能将命名的构造函数设为私有,因为否则将无处添加_前缀。

它用于防止从public factory构造函数以外的任何地方创建实例。这样可以确保您的应用程序中不能有多个Logger实例。 工厂构造函数仅在首次创建实例时使用,对于后续调用始终返回先前创建的实例。

答案 1 :(得分:3)

Dart 支持工厂构造函数,它可以返回子类型甚至 null。要创建工厂构造函数,请使用 factory 关键字:

class Square extends Shape {}

class Circle extends Shape {}

class Shape {
  Shape();

  factory Shape.fromTypeName(String typeName) {
    if (typeName == 'square') return Square();
    if (typeName == 'circle') return Circle();

    print('I don\'t recognize $typeName');
    return null;
  }
}

代码示例

填写名为 IntegerHolder.fromList 的工厂构造函数,使其执行以下操作:

  • 如果列表具有一个值,请使用该值创建一个 IntegerSingle
  • 如果列表有两个值,请按顺序创建一个IntegerDouble
  • 如果列表有三个值,请按顺序创建一个IntegerTriple
  • 否则,返回 null。

解决方案

class IntegerHolder {
  IntegerHolder();
  
  factory IntegerHolder.fromList(List<int> list) {
    if (list?.length == 1) {
      return IntegerSingle(list[0]);
    } else if (list?.length == 2) {
      return IntegerDouble(list[0], list[1]);
    } else if (list?.length == 3) {
      return IntegerTriple(list[0], list[1], list[2]);
    } else {
      return null;
    } 
  }
}

class IntegerSingle extends IntegerHolder {
  final int a;
  IntegerSingle(this.a); 
}

class IntegerDouble extends IntegerHolder {
  final int a;
  final int b;
  IntegerDouble(this.a, this.b); 
}

class IntegerTriple extends IntegerHolder {
  final int a;
  final int b;
  final int c;
  IntegerTriple(this.a, this.b, this.c); 
}

答案 2 :(得分:2)

tl; dr 在不需要 想要返回类本身的 new 实例的情况下使用工厂。用例:

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

说明

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 class,它定义了数十个返回不同子类的命名工厂构造函数。例如,Element.div()Element.li()分别返回<div><li>元素。

在这个缓存应用程序中,我发现“ factory”有点用词不当,因为它的目的是避免调用生成的构造函数,并且我认为现实世界中的工厂本质上是生成的。也许在这里更合适的术语是“仓库”:如果某件商品已经可用,请将其从货架上取下并交付。如果没有,请致电一个新电话。

这与命名构造函数有何关系?生成构造函数和工厂构造函数都可以不命名,也可以命名:

...
  // 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");
}


答案 3 :(得分:1)

作为Dave的答案的补充,此代码显示了使用factory返回父级相关类时的清晰示例。

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

您可以在此处运行此代码。 https://dartpad.dartlang.org/63e040a3fd682e191af40f6427eaf9ef

进行一些更改,以了解在某些情况下(例如单例)它如何工作。

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

答案 4 :(得分:1)

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

普通构造函数中,将创建一个实例,并使用初始值设定项列表实例化最终变量。这就是为什么没有return语句的原因。执行构造函数时,要返回的实例已经固定

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

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

// C++
MyCoolService.getInstance()

// Dart
MyCoolService()