【问题标题】:Understanding Factory constructor code example - Dart了解工厂构造函数代码示例 - Dart
【发布时间】:2019-05-22 00:22:38
【问题描述】:

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

  1. 为什么我应该在这个例子中使用factory
  2. 这是一个正在使用的命名构造函数吗?为什么?

【问题讨论】:

标签: constructor dart


【解决方案1】:

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】:

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

    普通构造函数中,会创建一个实例,并使用初始化列表对最终变量进行实例化。这就是为什么没有return 声明的原因。 返回的实例已经固定,在执行构造函数时!

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

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

    // C++
    MyCoolService.getInstance()
    
    // Dart
    MyCoolService()
    

    【讨论】:

      【解决方案3】:

      作为 Dave 的回答的补充,此代码显示了使用工厂返回父相关类时的一个清晰示例。

      看看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】:

        tl;dr 在您必要不希望返回类本身的实例的情况下使用工厂。用例:

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

        说明

        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 关键字。为了让工厂返回一个新的类实例,它必须首先调用一个生成构造函数。

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

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

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

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

        这一切与命名构造函数有何关系?生成式和工厂式构造函数都可以是未命名的或命名的:

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

        【讨论】:

        • 那么静态和工厂有什么区别??
        • static 可以引用类范围的方法或变量,而factory 仅引用构造函数方法。 IOW,您不能调用静态方法来构造类的实例,也不能调用工厂方法来执行除此之外的任何操作。唯一的相似之处是他们都无法访问this
        • you cannot invoke a static method to construct an instance of a class -- 但是static A get instance() =&gt; A();是做什么的,不是返回类的实例吗?
        【解决方案5】:

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

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

        可以使用 new 调用工厂构造函数,但这与 Dart 2 中的可选 new 几乎无关。

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

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

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

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

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

        【讨论】:

        • 先生,您的最后一行The factory constructor only creates ... previously created instance 总是正确的吗?我认为这取决于我们如何处理缓存。 factory 构造函数应该只创建一次实例,但也可以多次创建,这就是我的想法。如果我错了,请纠正我。谢谢
        • 如果以这种方式实现,这只是一个可能的用例,而不是一般事实。 ("它是用来防止...")
        猜你喜欢
        • 2014-02-18
        • 2014-11-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多