Sunday, February 14, 2016

The Chain of Responsibility is Better with Mixins


Having spent the entirety of last night's post lamenting the practice of using pattern-specific naming conventions, tonight I… use some pattern specific naming conventions. Well, maybe.

Tonight, I try out mixins as a possible implementation for the chain of responsibility pattern. I am unsure how it will work, but before trying it, it seems as though I will need to use patterns names in the mixin class.

Something that I hope to explore in Design Patterns in Dart are Dart twists on design patterns. On classic OO patterns, that should involve mixins. That said, the main reason that I think mixins might be appropriate in this case is that the example that I adapted from the Wikipedia pages bothers me:
  var manager = new ManagerPurchasePower();
  var director = new DirectorPurchasePower();
  var vp = new VicePresidentPurchasePower();
  var president = new PresidentPurchasePower();
I realize that this is example code, but I am hard-pressed to think that I would ever code up objects named "purchasing power." It seem much more likely that I would create Manager, Director, etc. classes with purchasing power traits. Hence, the thinking that mixins might be a good solution.

A more real-world example, I think, would be an Employee class:
class Employee {
  String firstName, lastName;
  Employee reportsTo;
}
The Manager, Director classes would then extend this class:
class Manager extends Employee {
  // Manager specializations here...
}
class Director extends Employee {
  // Director specializations here..
}
// ...
In this scenario, a purchasing power chain of responsibility can be described with:
abstract class _PurchasePower {
  get successor => reportsTo;

  void processRequest(PurchaseRequest request) {
    if (_processRequest(request)) return;
    if (successor == null) return;

    successor.processRequest(request);
  }

  bool _processRequest(_) => false;
}
The successor in this chain is delegated to the reportsTo property of Employee. I could also have replaced subsequent references to successor with reportsTo and the code would have remained equally readable. I opt to stick with the pattern participant name (successor) as a default starting point. The domain naming convention from last night may very well prove more maintainable. Then again, since this mixin class exists solely to implement the chain of responsibility pattern, perhaps its domain is really the pattern itself. Unsure, I opt for the pattern name for now.

In handle-request method, I first try to process the request with a subclass overrideable _processRequest() method. If the subclass' _processRequest() handles the request, it returns true, breaking the chain. If the subclass' implementation returns false (or the default implementation is invoked), then the next item in the chain is asked to handle the request.

Armed with this _PurchasePower class, I can mix it into the subclasses of Employee with the with keyword:
class Manager extends Employee with _PurchasePower {
  final double _allowable = 10 * 1000.0;

  bool _processRequest(PurchaseRequest request) {
    if (request.amount > _allowable) return false;

    print("Manager will approve $request.");
    return true;
  }
}
For the Manager class, the purchasing power traits are expressed by the overridden _processRequest() method. If the purchase request amount is more than $10,000, then the manager is unable to approve the request. The chain of responsibility is then passed back to the processRequest() method in the _PurchasePower mixin. In that case the successor / reportsTo is then asked to handle the request.

The successor chain is expressed in the Employee class, though technically it is only in the _PurchasePower mixin that this relationship recognized as a chain. The actual relationship is established in client code (alternatively, last night's solution can establish the relationship in the classes):
  var manager = new Manager();
  var director = new Director();

  manager.reportsTo = director;
Although I remain unsure of some of the specifics like naming, I very much appreciate this mixins approach. In hindsight, the original class names (e.g. ManagerPurchasingPower) were probably screaming at me that a mixin was trying to escape from my code. Regardless, encapsulating the pattern entirely in a mixin class while limiting Employee subclasses to employee-specific responsibilities feels like clean, maintainable code. So this feels very much like a win.

Play with the code on DartPad: https://dartpad.dartlang.org/752e601e8768cb2f26e5.


Day #95

No comments:

Post a Comment