【问题标题】:dart: wrap all function callsdart:包装所有函数调用
【发布时间】:2015-06-10 10:21:56
【问题描述】:

我正在尝试编写同一程序的两个版本:

  • 高性能版本;和
  • 一个较慢的版本,让用户知道发生了什么。

我想这与 IDE 实现正常/调试模式的方式并不完全不同。

我的要求,按重要性降序排列如下:

  1. 慢速版本应该产生与高性能版本相同的结果;
  2. 慢速版本应包装高性能版本进行的公共函数调用的子集;
  3. 对较慢版本的要求不应对高性能版本的性能产生不利影响;
  4. 最好不要代码复制,但必要时自动复制;
  5. 代码库大小的最小增加;和
  6. 理想情况下,慢速版本应该能够单独打包(可能单向依赖于高性能版本)

我知道要求 6 可能是不可能的,因为要求 2 需要访问类的实现细节(对于公共函数调用另一个公共函数的情况)。

为了便于讨论,请考虑以下程序的高性能版本来讲述一个简单的故事。

class StoryTeller{
  void tellBeginning() => print('This story involves many characters.');

  void tellMiddle() => print('After a while, the plot thickens.');

  void tellEnd() => print('The characters resolve their issues.');

  void tellStory(){
    tellBeginning();
    tellMiddle();
    tellEnd();
  }
}

带有镜像的简单实现,如下所示:

class Wrapper{
  _wrap(Function f, Symbol s){
    var name = MirrorSystem.getName(s);
    print('Entering $name');
    var result = f();
    print('Leaving $name');
    return result;
  }
}

@proxy
class StoryTellerProxy extends Wrapper implements StoryTeller{
  final InstanceMirror mirror;

  StoryTellerProxy(StoryTeller storyTeller): mirror = reflect(storyTeller);

  @override
  noSuchMethod(Invocation invocation) =>
      _wrap(() => mirror.delegate(invocation), invocation.memberName);
}

我喜欢这个解决方案的优雅,因为我可以更改高性能版本的界面,这很有效。不幸的是,它不能满足要求 2,因为 tellStory() 的内部调用没有被包装。

存在一个简单但更详细的解决方案:

class StoryTellerVerbose extends StoryTeller with Wrapper{
  void tellBeginning() => _wrap(() => super.tellBeginning(), #tellBeginning);
  void tellMiddle() => _wrap(() => super.tellMiddle(), #tellMiddle);
  void tellEnd() => _wrap(() => super.tellEnd(), #tellEnd);
  void tellStory() => _wrap(() => super.tellStory(), #tellStory);
}

这段代码可以很容易地使用镜像自动生成,但它会导致代码库大小大幅增加,特别是如果高性能版本具有广泛的类层次结构并且我希望有一个 const 类似于 const 变量类树深处的类。

另外,如果任何类没有公共构造函数,这种方法会阻止包的分离(我认为)。

我还考虑过使用 wrap 方法包装基类的所有方法,而高性能版本具有一个简单的 wrap 函数。但是,我担心这会对高性能版本的性能产生不利影响,特别是如果 wrap 方法需要调用作为输入。我也不喜欢这样一个事实,即这在本质上将我的高性能版本与慢速版本联系起来。在我的脑海中,我在想一定有一种方法可以让较慢的版本成为高性能版本的扩展,而不是两个版本都是一些更通用的超级版本的扩展。

我是否遗漏了一些非常明显的东西?是否有内置的“anySuchMethod”或类似的?我希望将代理解决方案的优雅与详细解决方案的完整性结合起来。

【问题讨论】:

  • 这适用于客户端和/或服务器端代码吗?
  • 这是客户端代码
  • 那我会尽量避免反思。 Dart 团队正在努力为客户端的反射提供更好的支持,但目前使用反射时很难获得合理的构建输出大小。

标签: dart proxy-classes dart-mirrors nosuchmethod


【解决方案1】:

您可以尝试将额外的调试代码放在 asserts(...) 中。当不在检查模式下运行时,它会自动删除。另见

否则只需创建一个全局常量 (const bool isSlow = true/false;) 在任何地方使用接口和工厂构造函数,它们根据isSlow 值返回接口的慢速或快速实现。 慢版本可以只扩展快速版本以重用其功能并通过覆盖其方法来扩展它。
这样您就不必使用会导致代码膨胀的镜像,至少对于客户端代码而言是这样。
当你构建时,所有不必要的代码都会被 tree-shaking 删除,这取决于 isSlow 的设置。 使用依赖注入有助于简化这种开发不同实现的方式。

【讨论】:

  • 认为可行。只是想检查一下我没有遗漏一些明显的东西。没想到 dart2js 这么聪明。谢谢。
猜你喜欢
  • 2021-04-22
  • 2017-09-13
  • 1970-01-01
  • 2016-09-02
  • 1970-01-01
  • 2023-01-16
  • 1970-01-01
  • 2011-04-24
  • 2017-02-05
相关资源
最近更新 更多