dart:包装所有函数调用

时间:2015-04-06 01:23:26

标签: dart proxy-classes dart-mirrors nosuchmethod

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

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

我认为它与IDE如何实现正常/调试模式并不完全不同。

我的要求按重要性递减顺序如下:

  1. 慢版本应该与高性能版本产生相同的结果;
  2. 慢版本应该包含由性能版本生成的公共函数调用的子集;
  3. 对较慢版本的要求不应对性能版本的性能产生负面影响;
  4. 最好不进行代码复制,但必要时自动复制;
  5. 代码库大小的最小增加;和
  6. 理想情况下,慢速版本应该能够单独打包(大概是对性能版本的单向依赖)
  7. 我理解需求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方法要求将调用作为输入。我也不喜欢这个内在地将我的高性能版本链接到慢速版本的事实。在我看来,我认为必须有一种方法可以使较慢的版本成为性能版本的扩展,而不是两个版本都是一些更通用的超级版本的扩展。

    我错过了一些非常明显的东西吗?是否有内置的'anySuchMethod'或其他一些?我希望将代理解决方案的优雅与详细解决方案的完整性结合起来。

1 个答案:

答案 0 :(得分:1)

您可以尝试将其他调试代码放在asserts(...)中。未在选中模式下运行时会自动删除。另见

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