Tuesday, February 16, 2016

In Which I Take Things One Step Too Far...


So the chain of responsibility pattern in Dart… I've explored it with noSuchMethod(). I've explored it with mixins. But I have yet to explore it with noSuchMethod() in mixins.

Things are gonna get a little crazy tonight.

I continue to work with the _PurchasePower mixin for employee-like classes. It describes the ability of an employee to make purchasing decisions. It sets the successor in the responsibility chain to point to the employee next on the corporate ladder:
abstract class _PurchasePower {
  get successor => reportsTo;
  // ...
}
In this case, it simply delegates to the reportsTo property of Employee—the receiver of the mixin.

The _PurchasePower mixin completes the chain of responsibility pattern by declaring processRequest(). This method sends the request to the successor if the current class does not handle it, and _processRequest(), which performs the actual handling of the request:
abstract class _PurchasePower {
  // ...
  void processRequest(PurchaseRequest request) {
    if (_processRequest(request)) return;
    if (successor == null) return;

    successor.processRequest(request);
  }

  bool _processRequest(_) => false;
}
That works great when there is only one request to handle, but what if there are multiple requests to be handled (e.g. processorName() and processRequest())?

This is where it would be handy to be able to mixin noSuchMethod(). Instead of declaring processRequest() to possibly send the request to the successor, noSuchMethod() can do so. Except I hit a stumbling block right away here.

First, I need to handle requests by converting processRequest() to _processRequest() and processorName() to _processorName(). Just prepend an underscore, right? Well, no.

When I get the arguments inside noSuchMethod, I can figure out the method that was being invoked by checking memberName. From here, it should be a simple matter of interpolating noSuchMethod's memberName into a string:
  noSuchMethod(args) {
    var handlerMethod = "_${args.memberName}";
    // ...
  }
The problem is that memberName is a Symbol, which gets converted to strings as "Symbol('symbol_name_here'). So the handlerMethod winds up being _Symbol('_processRequest'). Not at all what I want.

I have two undesirable choices here—strip Symbol(' and ') from the resulting string or painstakingly lookup the string value. Neither is a good option, but the latter seems to be the most official way:
  noSuchMethod(args) {
    var handlerMethod = "_${MirrorSystem.getName(args.memberName)}";
    // ...
  }
Yuck. And unfortunately, I am not done with the yuck. I cannot invoke methods with strings, so I have to convert that back to a Symbol:
  noSuchMethod(args) {
    Symbol handlerMethod = new Symbol("_${MirrorSystem.getName(args.memberName)}");
    // ...
  }
Even now, I am not done with the yuck. And this next yuckiness takes me a while to track down.

It turns out that you cannot use the Symbol constructor to create a symbol that is private. Well, that is not 100% true, it lets me create a symbol, but I am not allowed to use it to invoked the private method. Making this situation even more annoying is that I can invoke _processRequest() with a symbol literal: #_processRequest. But if I dynamically try to create that symbol, it silently fails to call the right method.

Bummer.

Even if I choose a different naming convention, I would not want the method that actually processes the request to be public. Only the method invoking the handler or sending the request to the successor should be public. So I am left to either move handler code into noSuchMethod() to sit beside the successor code or I can give the handler method a weird name.

I opt for the latter by prepending xxx_ to the name:
  noSuchMethod(args) {
    Symbol handlerMethod = new Symbol("xxx_${MirrorSystem.getName(args.memberName)}");
    // ...
  }
With this approach, I eventually get the code working, but... yuck.

Even if this were back in the Employee class, I would still face the same limitation from the Symbol constructor. In other words, this seems like an ill-advised approach in current Dart.

Play with the code on DartPad: https://dartpad.dartlang.org/9f9ca7397d88f6cf7e54.



Day #97

No comments:

Post a Comment