带有工厂构造函数的抽象 class 的好处?

Benefits of an abstract class with a factory constructor?

我最近 运行 研究了一些示例,这些示例使用抽象 类 作为接口,但也向抽象接口添加了工厂构造函数,因此在某种意义上它可以是 "newed"向上。例如:

abstract class WidgetService {
    factory WidgetService() = ConcreteWidgetService;

    Widget getWidget();
    void saveWidget(Widget widget);
}

class ConcreteWidgetService extends BaseWidgetService implements WidgetService {

    WidgetService();

    Widget getWidget() {
        // code to get widget here
    }

    void saveWidget(Widget widget) {
        // code to save widget here
    }
}

此服务的用法将在其他一些服务或组件中使用,如下所示:

WidgetService _service = new WidgetService();

根据我对这个示例的理解,上面的行基本上"new" 了一个 WidgetService,它通常会从 Dart 分析器中产生一个警告,并且所说的服务变量实际上是一个基于 ConcreateWidgetService 的实例将 ConcreateWidgetService 分配给 WidgetService 的工厂构造函数。

这种方法有什么好处吗?根据我的 OOP 经验,当我不知道我将获得的具体类型时,我使用 abstract classes/interfaces 进行编程。在这里,我们似乎将具体类型立即分配给抽象工厂构造函数。我想我的第二个问题是在这种情况下为什么不直接在这里使用 ConcreteWidgetService 而不是重复所有方法签名?

在 Dart 中,所有 classes 也自动成为接口。创建一个 - 'newing up' - 一个 'interface' 的实例并没有什么奇怪的,因为它实际上只是创建一个 class.

的实例

一些抽象 classes 的目的是专门定义一个接口,但它们具有 return 一个 'default implementation' 自己的工厂构造函数。

例如:

void main() {
  var v = new Map();
  print(v.runtimeType);
}

打印:_InternalLinkedHashMap - Map.

的(当前)默认实现

核心库用户可以创建和使用 Map 的实例,而无需了解或关心他们实际获得的实现。库作者可以在不破坏任何人代码的情况下更改实现。

当然,其他 classes 也可以实现 Map

关于您的代码示例,WidgetService _service = new WidgetService(); 不会产生分析器警告。请参阅此 example on DartPad(请注意,我修复了您示例中的几个错误)。

我最初对以下内容感到困惑:

factory WidgetService() = ConcreteWidgetService;

而不是:

factory WidgetService() => new ConcreteWidgetService();

但它似乎有效。

在您的示例中,好处是有限的,但在更复杂的情况下,好处会变得更加明显。

工厂构造函数允许您更好地控制构造函数 return 的内容。它可以 return 子类的实例或已经存在的(缓存的)实例。

它可以return基于构造函数参数的不同具体实现:

abstract class WidgetService {
  WidgetService _cached;

  factory WidgetService(String type) {
    switch (type) {
      case 'a':
        return ConcreteWidgetServiceA();
      case 'b':
        return ConcreteWidgetServiceB();
      default:
        return _cached ??= DummyWidgetServiceA();
    }
  }

  Widget getWidget();

  void saveWidget(Widget widget);
}

您的示例似乎是为最终扩展到这种更灵活的方法做准备。