dart:包装所有函数调用

dart:包装所有函数调用,dart,proxy-classes,dart-mirrors,nosuchmethod,Dart,Proxy Classes,Dart Mirrors,Nosuchmethod,我正在尝试编写同一程序的两个版本: 表演版;及 一个较慢的版本,让用户知道发生了什么 我想这与IDE实现普通/调试模式的方式并不完全不同 我的要求按重要性的降序排列如下: 慢速版本应产生与性能版本相同的结果 慢版本应该包装performant版本发出的公共函数调用的子集 较慢版本的要求不应对性能版本的性能产生不利影响 最好无代码复制,但在必要时自动复制 代码库大小的最小增加;及 理想情况下,慢速版本应该能够单独打包(可能是单向依赖于performant版本) 我理解要求6可能是不可能的,因为

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

  • 表演版;及
  • 一个较慢的版本,让用户知道发生了什么
我想这与IDE实现普通/调试模式的方式并不完全不同

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

  • 慢速版本应产生与性能版本相同的结果
  • 慢版本应该包装performant版本发出的公共函数调用的子集
  • 较慢版本的要求不应对性能版本的性能产生不利影响
  • 最好无代码复制,但在必要时自动复制
  • 代码库大小的最小增加;及
  • 理想情况下,慢速版本应该能够单独打包(可能是单向依赖于performant版本)
  • 我理解要求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);
    }
    
    我喜欢这个解决方案的优雅,因为我可以更改performant版本的界面,这一切都可以正常工作。不幸的是,它无法满足要求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);
    }
    
    这段代码可以很容易地使用镜像自动生成,但它会导致代码基大小的大幅增加,特别是如果performant版本具有广泛的类层次结构,并且我希望在类树的深处有一个与类的常量变量类似的常量

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

    我还考虑过用wrap方法包装基类的所有方法,performant版本有一个简单的wrap函数。但是,我担心这会对performant版本的性能产生负面影响,特别是如果wrap方法需要(比如)调用作为输入。我也不喜欢这样一个事实,即我的高性能版本与低性能版本之间存在内在联系。在我看来,我想一定有办法让较慢的版本成为performant版本的扩展,而不是两个版本都是一些更通用的超级版本的扩展


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

    您可以尝试将额外的调试代码放在断言中(…)。当未在选中模式下运行时,会自动删除此选项。另见

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

    这是针对客户端和/或服务器端代码的吗?这是针对客户端代码的,我会尽量避免反射。Dart团队正致力于在客户端更好地支持反射,但目前在使用反射时,很难获得合理的构建输出大小。我想这是可行的。我只是想确认一下我没有漏掉什么明显的东西。没想到dart2js这么聪明。谢谢