当两个 类 相互依赖时 GetIt 插件堆栈溢出错误

GetIt plugin Stack Overflow Error when two classes depend on each other

当我有以下设计时,我得到堆栈溢出错误:

我知道问题出在第一个虚线箭头上。

代码如下:

import 'package:flutter/material.dart';
import 'package:minimalist_timer_app/widgets/timer_container/timer_container_controller.dart';
import 'package:minimalist_timer_app/services/service_locator.dart';

class TimerContainer extends StatefulWidget {
  const TimerContainer({Key? key}) : super(key: key);

  @override
  State<TimerContainer> createState() => _TimerContainerState();
}

class _TimerContainerState extends State<TimerContainer> {
  @override
  void initState() {
    super.initState();
    _widgetController.init();
  }

  final TimerContainerController _widgetController = getIt<TimerContainerController>();
  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<String>(
        valueListenable: _widgetController,
        builder: (_, timer, __) {
          return Text(
            timer,
            style: const TextStyle(fontSize: 70, fontWeight: FontWeight.w700),
          );
        });
  }
}
import 'package:flutter/material.dart';
import 'package:minimalist_timer_app/services/service_locator.dart';
import 'package:minimalist_timer_app/services/timer_service.dart';

class TimerContainerController extends ValueNotifier<String> {
  TimerContainerController() : super(defaultTimerString);
  final _timerService = getIt<TimerService>();

  init() => _timerService.init();
}
import 'package:flutter/material.dart';
import 'package:minimalist_timer_app/services/service_locator.dart';
import 'package:minimalist_timer_app/widgets/timer_container/timer_container_controller.dart';

class TimerService {
  final _timerNotifier = getIt<TimerContainerController>();

  init() {
    // ...
    _timerNotifier.value = _newString;
  }
}

我还找到了一个解决方法:如果我没有在其中一个文件中声明 _timerNotifier 或 _timerService,问题就消失了,那么我必须像这样更改我的代码:

因此,换句话说,两者不能同时声明,因为它们相互针对。

问题是,这里的最佳做法是什么?我的解决方法是可行的(不声明变量)吗?还是我的设计有问题?如果可以,我该如何修改?

最佳做法是不要有循环依赖。没有好的干净的方法来做你正在做的事情,所以最好的办法是重新设计你的架构,首先不要出现这个问题。

如果出于某种原因你需要做这样的事情,将getIt调用移动到你实际需要服务的方法而不是试图缓存字段初始值设定项或构造函数中的值(无论如何都违背了 get_it 的目的):

  • timer_container_controller.dart
class TimerContainerController extends ValueNotifier<String> {
  TimerContainerController() : super(defaultTimerString);

  init() {
    final _timerService = getIt<TimerService>();
    _timerService.init();
  }
}
  • timer_service.dart
class TimerService {
  init() {
    // ...

    final _timerNotifier = getIt<TimerContainerController>();
    _timerNotifier.value = _newString;
  }
}

但是,我再一次强调,你必须这样做的事实表明代码有异味,并且你最好使用完全不同的方法,其中两个独立的 classes 不依赖于彼此进行初始化。


编辑:

你还没有完全理解逻辑和数据分离背后的意义,所以我将把它作为对这个答案的编辑,希望我能在没有字符限制的情况下完美清晰地描绘图片和有限的格式。

在您的回答中,选项 A 实际上与您正在做的更多相同,尽管删除了循环依赖性。您仍然有一个 class TimerController 负责处理逻辑和数据。这违反了 single-responsibility,因为每个 class 应该只有一份工作。

选项 B 是朝着正确方向迈出的一步,更类似于 Suragch 的示例在他们的文章中所做的,但您仍然没有掌握 dependency-inversion 背后的概念或服务的目的是什么.

如果你想象一个依赖层次结构,UI 在顶部,逻辑在 UI 下面,所以 UI 依赖于逻辑,但逻辑并不特别关心 UI。下面是数据,其中逻辑(以及扩展 UI)取决于数据,但数据不关心逻辑或 UI。在这个层次结构中,你不能让任何层直接调用它上面的层中的任何东西,这样做违反了 dependency-inversion*。比如不能让数据层直接调用逻辑层的函数。

现在想象一下该层次结构中更低的服务。根据定义,服务只负责它们自己的操作,而不负责其他任何事情。您不能让它们依赖于任何其他层,也不能让它们直接调用其他对象的成员,因为这违背了服务作为服务的全部目的。


我认为另一个混淆来源是“直接”和“间接”调用之间的区别。 直接调用 意味着代码 A 直接调用 class B 的成员,因为它有一个 class 的实例并且正在显式调用该成员。 间接调用 意味着调用是隐式发生的,因为代码 A 可能正在调用 class B 的成员,但它不是通过 [=140] 的实例直接调用=] B. 下面是一些实际的例子:

// Direct invocations
final foo = new XYZ();
foo.a = 'something';
foo.doSomething();

final bar = getIt<Bar>();
final value = bar.getAValue();

// Indirect invocations
void useCallback(Function func) {
  func('some value');
}

void useStream() {
  this.streamController.add('some value');
}

void useNotifier() {
  this.value = 'some value';
}

请注意所有直接调用如何涉及被调用的实际对象,无论该对象来自手动构造还是服务定位器调用。这就是直接调用的原因——代码是 直接 调用该对象的某些成员。相比之下,间接 调用不会显式调用某个对象的成员,而是调用某个函数或发送数据而不假设接收者是什么。

有几种不同类型的间接调用,示例显示了几种:

  • 回调是源可以提供的函数,作为一种“相关事件发生时调用此函数”。在此示例中,任何调用 useCallback 方法的代码都负责提供回调。 useCallback 方法本身?它不关心回调来自哪里。它只是调用它。

  • 流有点像持续的回调,同时也是类固醇。负责处理流和将数据发送到流中的代码并不关心另一端可能在监听什么——甚至根本没有监听。它只是将数据推送到一端,然后继续它的一天。

  • ValueNotifier 通过为小部件提供可选订阅的事件总线,间接调用小部件重建自身。它不会单独访问每个小部件并手动告诉它重建,它只是触发事件并将其留给监听小部件来做出反应。


考虑到这些,我将如何构建您的代码:

(注意:我不知道你的一些设计决策的最终目标是什么,所以如果我想更多地了解你的最终目标,我很可能会进入完全不同的方向。例如,我有不知道计时器服务中字符串的用途是什么,但如果它应该是计时器的当前值,我建议将其保留为数字并让侦听器负责将其转换为字符串。)

  • timer_container.dart
import 'package:flutter/material.dart';
import './timer_container_controller.dart';

class TimerContainer extends StatefulWidget {
  const TimerContainer({Key? key}) : super(key: key);

  @override
  State<TimerContainer> createState() => _TimerContainerState();
}

class _TimerContainerState extends State<TimerContainer> {
  final _widgetController = getIt<TimerContainerController>();

  @override
  void initState() {
    super.initState();
    _widgetController.init();
  }

  @override
  void dispose() {
    _widgetController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<String>(
      valueListenable: _widgetController.timerNotifier,
      builder: (_, timer, __) {
        return Text(
          timer,
          style: const TextStyle(
            fontSize: 70, 
            fontWeight: 
            FontWeight.w700,
          ),
        );
      },
    );
  }
}
  • timer_container_controller.dart
import './timer_container_notifier.dart';

class TimerContainerController {
  final timerNotifier = TimerContainerNotifier();

  init() {
    timerNotifier.init();
  }

  dispose() {
    timerNotifier.dispose();
  }

  // ... various other action handlers from the UI
}
  • timer_container_notifier.dart
import './services/timer_service.dart';

class TimerContainerNotifier extends ValueNotifier<String> {
  timerService = TimerService(); // Or alternatively, `getIt<TimerService>()`

  TimerContainerNotifier() : super(mkDefaultTimerString);

  void init() {
    timerService.init(value);
    timerService.onTick = onTimerTick;
  }

  void dispose() {
    // Whatever clean-up necessary here
    // possibly `timerService.stop`
  }

  void onTimerTick(String newValue) {
    value = newValue;
  }

  // ... Various other data events
  • timer_service.dart
class TimerService {
  String timerValue; // Shouldn't this be a number, though?
  void Function(String) onTick;

  init(String initialValue) {
    _timerValue = initialValue;
  }

  // ... Various other timer actions
}

一个简单的看什么的方法class取决于看什么是imports。看看容器怎么导入控制器,控制器导入通知器,通知器导入服务,而服务根本没有导入?这是反映 UI、逻辑、数据和服务之间理想分离的简单层次结构。


*: 这是对 dependency-inversion 原则的过度简化,因为它属于简约方法。实际上,严格遵守 dependency-inversion 意味着没有任何东西直接依赖于其他任何东西,而是依赖性附加到源 class 为其提供实现的松散抽象。不过,对于一个简单的应用程序来说,这有点矫枉过正,而且一旦您超过一定程度的依从性,returns 的实用性就会迅速下降。

免责声明: @Abion47。多亏了他的指导、时间、奉献和关怀,我才得以找到解决方案。所以,这些想法主要来自@Abion47——不是我。

问题

设计有误。出现循环依赖错误,如果设计正确就不会出现。

解决方法有效,但是,最好先更改设计。除非更改设计,否则这种错误会一直发生。

不正确的设计决策是:

  • 一个。混合数据和逻辑层
  • 乙。服务层在示例中并不是真正的服务

一个。混合数据和逻辑层

  1. 它被称为控制器层,但它实际上是一个数据层

  2. 根据上一点,因此与其将TimerContainerController用作class和文件名,不如将其命名为TimerNotifier更合适

  3. 根据前面的几点,很明显控制器和数据层混合为一层(称为控制器层),而它们的关注点应该分开:

    • 数据层只关心数据(回调、流、服务等间接调用除外)
    • 逻辑层(控制器)只关心逻辑
  4. 应该有一个看起来像这样的新控制器:

  • timer_container_controller.dart
import 'package:minimalist_timer_app/widgets/timer_container/timer_notifier.dart';

class TimerContainerController {
  final timerNotifier = getIt<TimerNotifier>();

  init() => timerNotifier.init();
}

乙。服务层并不是真正的服务

  1. 服务是独立的,不应与其自身之外的任何其他 files/classes 紧密耦合(想象一个网络 api)。它们不依赖于任何应用程序文件(不应该有使它们依赖于其他应用程序文件的导入 data/file)。

  2. 说到1.点,服务不知道,也不关心是谁调用的。服务完成它的工作,return 它需要的任何东西 return 到它调用它的任何人。

  3. 因此,我们必须删除依赖项(以及导入): final _timerNotifier = getIt<TimerContainerController>();

  4. 由于它是独立的,它不能像这里_timerNotifier.value = new;那样直接更改其他file/layer中的数据,因此也必须将其删除。

  5. 相反,服务应该做什么,就应该return做什么。所以,让它 return 像这样的新值:

String init() {
    // ...
    return new;
}

解决方案

修复错误的设计决策,我找到了三种不同的可能设计解决方案:

解决方案 A1

  • init():逻辑层执行init()
  • 数据层: 分隔在变量final timerNotifier = ValueNotifier<String>();

  • timer_container.dart(UI层)
import 'package:flutter/material.dart';
import 'package:minimalist_timer_app/widgets/timer_container/timer_container_controller.dart';
import 'package:minimalist_timer_app/services/service_locator.dart';

class TimerContainer extends StatefulWidget {
  const TimerContainer({Key? key}) : super(key: key);

  @override
  State<TimerContainer> createState() => _TimerContainerState();
}

class _TimerContainerState extends State<TimerContainer> {
  final TimerContainerController _widgetController = getIt<TimerContainerController>();

  @override
  void initState() {
    super.initState();
    _widgetController.init();
  }

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<String>(
        valueListenable: _widgetController.timerNotifier,
        builder: (_, timer, __) {
          return Text(timer);
        });
  }
}
  • timer_container_controller.dart(逻辑+数据层)
import 'package:minimalist_timer_app/services/service_locator.dart';
import 'package:minimalist_timer_app/services/timer_service.dart';

class TimerContainerController {
  final timerNotifier = ValueNotifier<String>(mkDefaultTimerString);
  final _timerService = getIt<TimerService>();

  init() {
    timerNotifier.value = _timerService.init();
  }
}
  • timer_service.dart(服务层)

class TimerService {

  String init() {
    // ...
    return _newString;
  }
}

选项 A2

  • 与A1非常相似
  • init(): 逻辑层也执行 init()
  • 数据层(与A1的区别):提取到新的file/class:final timerNotifier = getIt<TimerContainerNotifier>();

  • timer_container.dart(UI层)
import 'package:flutter/material.dart';
import 'package:minimalist_timer_app/widgets/timer_container/timer_container_controller.dart';
import 'package:minimalist_timer_app/services/service_locator.dart';

class TimerContainer extends StatefulWidget {
  const TimerContainer({Key? key}) : super(key: key);

  @override
  State<TimerContainer> createState() => _TimerContainerState();
}

class _TimerContainerState extends State<TimerContainer> {
  final TimerContainerController _widgetController = getIt<TimerContainerController>();

  @override
  void initState() {
    super.initState();
    _widgetController.init();
  }

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<String>(
        valueListenable: _widgetController.timerNotifier,
        builder: (_, timer, __) {
          return Text(timer);
        });
  }
}
  • timer_container_controller.dart(逻辑层)
import 'package:minimalist_timer_app/widgets/timer_container/timer_notifier.dart';
import 'package:minimalist_timer_app/services/service_locator.dart';
import 'package:minimalist_timer_app/services/timer_service.dart';

class TimerContainerController {
  final timerNotifier = ValueNotifier<String>(mkDefaultTimerString);
  final _timerService = getIt<TimerService>();

  init() {
    timerNotifier.value = _timerService.init();
  }
}
  • timer_container_notifier.dart(数据层)
import 'package:flutter/material.dart';

class TimerNotifier extends ValueNotifier<String> {
  TimerNotifier() : super(mkDefaultTimerString);
}
  • timer_service.dart(服务层)

class TimerService {

  String init() {
    // ...
    return _newString;
  }
}

选项 B

  • init():数据层执行init()
  • 数据层:提取到新的file/class:final timerNotifier = getIt<TimerContainerNotifier>();

  • timer_container.dart(UI层)
import 'package:flutter/material.dart';
import 'package:minimalist_timer_app/widgets/timer_container/timer_container_controller.dart';
import 'package:minimalist_timer_app/services/service_locator.dart';

class TimerContainer extends StatefulWidget {
  const TimerContainer({Key? key}) : super(key: key);

  @override
  State<TimerContainer> createState() => _TimerContainerState();
}

class _TimerContainerState extends State<TimerContainer> {
  final TimerContainerController _widgetController = getIt<TimerContainerController>();

  @override
  void initState() {
    super.initState();
    _widgetController.init();
  }

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<String>(
        valueListenable: _widgetController.timerNotifier,
        builder: (_, timer, __) {
          return Text(timer);
        });
  }
}
  • timer_container_controller.dart(逻辑层)
import 'package:minimalist_timer_app/widgets/timer_container/timer_notifier.dart';
import 'package:minimalist_timer_app/services/service_locator.dart';


class TimerContainerController {
  final timerNotifier = ValueNotifier<String>(mkDefaultTimerString);

  init() {
    timerNotifier.init();
  }
}
  • timer_container_notifier.dart(数据层)
import 'package:flutter/material.dart';
import 'package:minimalist_timer_app/services/timer_service.dart';

class TimerNotifier extends ValueNotifier<String> {
  TimerNotifier() : super(mkDefaultTimerString);

  init() {
    value = _timerService.init();
  }
}
  • timer_service.dart(服务层)

class TimerService {

  String init() {
    // ...
    return _newString;
  }
}

既然你参考了我的文章,State Management for Minimalists,这里补充几点:

  • 服务层应该对状态管理层一无所知。 ValueNotifier 是状态管理层的一部分,您的 TimerService 不应该引用它。相反,您的状态管理 class(对您来说是 TimerContainerController)应该在服务层上调用一些方法,并根据返回的数据,状态管理 class 将更新 ValueNotifier。将服务层想象成 Web 服务器。任何人都可以从 Web 服务器请求数据,但该 Web 服务器对正在访问它的 Web 客户端一无所知。这种架构也将防止循环依赖。
  • 我倾向于同意 Abion47 的观点,即“Controller”不是状态管理的最佳名称class,因为他们提到的原因。我最近一直称呼我的“经理”。但是只要你有正确的架构,名字并不是那么重要。