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

Posted

技术标签:

【中文标题】如何使用 dart 实现委托/代理?【英文标题】:How to use dart to implement a delegate/proxy? 【发布时间】: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

【问题讨论】:

【参考方案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());

【讨论】:

我可以使用cm.delegate(invocation) 代替cm.invoke(...) 吗?我试过他们都工作,但有什么区别? 与 InstanceMirror 不同,ClassMirror 上没有 delegate 方法。你可以file an issue for that。【参考方案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;
  

【讨论】:

有没有办法避免为每个类创建一个代理。

以上是关于如何使用 dart 实现委托/代理?的主要内容,如果未能解决你的问题,请参考以下文章

Objective-C中的委托(代理)模式

如何在 MonoTouch 中代理 UIKit 代表?

如何理解Spring中的IOC和AOP

理解委托代理

iOS 委托代理与协议(Delegate and Protocol)

设计模式——代理模式