【问题标题】:How to use dart to implement a delegate/proxy?如何使用 dart 实现委托/代理?
【发布时间】:2013-06-30 19:46:11
【问题描述】:

我有两个类ParserProxy,当我从Parser 调用一个不存在的方法时,它会将其委托给Proxy 类。

我的代码:

class Parser {
    noSuchMethod(Invocation invocation) {
        // how to pass the `invocation` to `Proxy`???
    }
}

class Proxy {
    static String hello() { return "hello"; }
    static String world() { return "world"; }
}

当我写的时候:

var parser = new Parser();
print(parser.hello());

它将打印:

hello

【问题讨论】:

    标签: proxy delegates dart dart-mirrors nosuchmethod


    【解决方案1】:

    你必须使用 dart:mirrors。方法如下:

    import 'dart:mirrors';
    
    class Parser {
      noSuchMethod(Invocation invocation) {
        ClassMirror cm = reflectClass(Proxy);
        return cm.invoke(invocation.memberName
            , invocation.positionalArguments
            /*, invocation.namedArguments*/ // not implemented yet
            ).reflectee;
      }
    }
    
    class Proxy {
      static String hello() { return "hello"; }
      static String world() { return "world"; }
    }
    
    main(){
      var parser = new Parser();
      print(parser.hello());
      print(parser.world());
    }
    

    【讨论】:

    【解决方案2】:

    Alexandre 的回答是正确的,但我想补充一点。

    我假设对Proxy 的委托是一个实现细节,我们不希望用户接触到它。在这种情况下,我们应该对在parser 上调用不受Proxy 支持的方法的情况进行一些处理。现在,如果你这样做:

    void main() {
      var parser = new Parser();
      print(parser.foo());
    }
    

    你得到这个错误:

    Unhandled exception:
    Compile-time error during mirrored execution: <Dart_Invoke: did not find static method 'Proxy.foo'.>
    

    我会在noSuchMethod 中编写代码稍有不同。在委托给Proxy 之前,我会检查Proxy 是否支持我将要调用的方法。如果Proxy 支持它,我将调用Proxy 上的方法,正如亚历山大在他的回答中所描述的那样。如果Proxy 不支持该方法,我会抛出NoSuchMethodError

    这是答案的修订版:

    import 'dart:mirrors';
    
    class Parser {
      noSuchMethod(Invocation invocation) {
        ClassMirror cm = reflectClass(Proxy);
        if (cm.methods.keys.contains(invocation.memberName)) {
          return cm.invoke(invocation.memberName
              , invocation.positionalArguments
              /*, invocation.namedArguments*/ // not implemented yet
              ).reflectee;
        }
        throw new NoSuchMethodError(this,
            _symbolToString(invocation.memberName),
            invocation.positionalArguments,
            _symbolMapToStringMap(invocation.namedArguments));
      }
    }
    
    
    String _symbolToString(Symbol symbol) => MirrorSystem.getName(symbol);
    
    Map<String, dynamic> _symbolMapToStringMap(Map<Symbol, dynamic> map) {
      if (map == null) return null;
      var result = new Map<String, dynamic>();
      map.forEach((Symbol key, value) {
        result[_symbolToString(key)] = value;
      });
      return result;
    }
    
    class Proxy {
      static String hello() { return "hello"; }
      static String world() { return "world"; }
    }
    
    main(){
      var parser = new Parser();
      print(parser.hello());
      print(parser.world());
      print(parser.foo());
    }
    

    这是运行此代码的输出:

    hello
    world
    Unhandled exception:
    NoSuchMethodError : method not found: 'foo'
    Receiver: Instance of 'Parser'
    Arguments: []
    

    【讨论】:

      【解决方案3】:

      我还要补充一点,如果您要委派给的一组事物是固定的并且您可以合理地对其进行硬编码,那么您可以避免使用镜像。如果您使用静态方法,这特别容易,但我不清楚您为什么在这里这样做。我认为以下方法适用于实例方法和静态方法,但我在没有实际尝试的情况下输入此代码...

      Function lookupMethod(Proxy p, Symbol name) {
        if (name == const Symbol("hello")) return p.hello;
        if (name == const Symbol("world")) return p.world;
        throw "Aaaaaagh";
      }
      
      noSuchMethod(invocation) => 
          Function.apply(lookupMethod(Proxy, invocation.memberName),
              invocation.positionalArguments);
      

      如果转发的方法集发生变化,这很脆弱,但如果您使用镜像(目前几乎禁用所有 tree-shaking),可能有助于避免代码大小增加。

      【讨论】:

        【解决方案4】:

        这个例子也可以帮助你理解:

        void main() {
          var car = new CarProxy(new ProxyObjectImpl('Car'));
          testCar(car);
        
          var person = new PersonProxy(new ProxyObjectImpl('Person'));
          testPerson(person);
        }
        
        void testCar(Car car) {
          print(car.motor);
        }
        
        void testPerson(Person person) {
          print(person.age);
          print(person.name);
        }
        
        abstract class Car {
          String get motor;
        }
        
        abstract class Person {
          int get age;
          String get name;
        }
        
        class CarProxy implements Car {
          final ProxyObject _proxy;
        
          CarProxy(this._proxy);
        
          noSuchMethod(Invocation invocation) {
            return _proxy.handle(invocation);
          }
        }
        
        class PersonProxy implements Person {
          final ProxyObject _proxy;
        
          PersonProxy(this._proxy);
        
          noSuchMethod(Invocation invocation) {
            return _proxy.handle(invocation);
          }
        }
        
        abstract class ProxyObject {
          dynamic handle(Invocation invocation);
        }
        
        class ProxyObjectImpl implements ProxyObject {
          String type;
          int id;
          Map<Symbol, dynamic> properties;
        
          ProxyObjectImpl(this.type, [this.id]) {
            properties = ProxyManager.getProperties(type);
          }
        
          dynamic handle(Invocation invocation) {
            var memberName = invocation.memberName;
        
            if(invocation.isGetter) {
              if(properties.containsKey(memberName)) {
                return properties[memberName];
              }
            }
        
            throw "Runtime Error: $type has no $memberName member";
          }
        }
        
        class ProxyManager {
          static Map<Symbol, dynamic> getProperties(String name) {
            Map<Symbol, dynamic> properties = new Map<Symbol, dynamic>();
            switch(name) {
              case 'Car':
                properties[new Symbol('motor')] = 'xPowerDrive2013';
                break;
              case 'Person':
                properties[new Symbol('age')] = 42;
                properties[new Symbol('name')] = 'Bobby';
                break;
              default:
                throw new StateError('Entity not found: $name');
            }
        
            return properties;
          }
        }
        

        【讨论】:

        • 有没有办法避免为每个类创建一个代理。
        猜你喜欢
        • 1970-01-01
        • 2019-10-07
        • 1970-01-01
        • 2013-12-05
        • 2011-08-21
        • 2013-11-26
        • 1970-01-01
        • 1970-01-01
        • 2019-10-10
        相关资源
        最近更新 更多