首页 文章

如何使用dart实现委托/代理?

提问于
浏览
4

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

我的代码:

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

4 回答

  • 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());
    }
    
  • 7

    亚历山大的答案是正确的,但我想补充一些东西 .

    我假设 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 上的方法,正如Alexandre在他的回答中所描述的那样 . 如果 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);
    

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

  • 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;
      }
    }
    

相关问题