Sunday, February 21, 2016

A Facade for the Chain of Responsibility


How much should the client know about the implementation of the chain of responsibility pattern? I have reworked my spreadsheet cell formatting example to the point that I am quite satisfied with the code organization and how it responds:



What leaves me slightly less satisfied is how much the Dart constructing code needs to know about the underlying implementation. Currently, I have it constructing the individual links in the chain and assigning those links:
  var textFormat = new TextFormatter();
  var dateFormat = new DateFormatter(textFormat);
  var numberFormat = new NumberFormatter(dateFormat);
I do not think it too horrible that the constructing code needs to know about the individual formatters—especially since it needs to know how to remove some of them in response to checkbox changes. What bugs me here is the same thing that has bugged me about other examples that I have built—the requirement that the constructing code know which link is first, which link has to be constructed first, and with which links each can and should link.

That is, the text-formatter is the default link, so it needs to be last in the chain, but it has to be constructed first so that the second-to-last link can point to it. It is also not a good idea to remove the text-formatter (even though I have a checkbox in the UI) because it is the default cell formatter. Lastly, I find it awkward to tell the on-change listener to number-format cells when establishing the request handler:
  container.onChange.listen(numberFormat);
Even though it looks like I am asking for number formatting, I am really asking for something in the cell formatting chain to handle the request—number format just happens to be the first.

In some (most?) implementations of the chain of responsibility this might actually be OK or desired organization. In this case, I think a facade wants to be in front of this complexity. So I define CellFormatter such that it declares the individual link components:
class CellFormatter {
  var _numberFormat = new NumberFormatter();
  var _dateFormat = new DateFormatter();
  var _textFormat = new TextFormatter();

  var first;
  // ...
}
I also declare a pointer to the first cell-formatting object so that I know where to start requests and can easily update it. In the constructor, I then establish the links in the chain and initially set first:
class CellFormatter {
  // ...
  CellFormatter() {
    _numberFormat.nextHandler = _dateFormat;
    _dateFormat.nextHandler = _textFormat;
    first = _numberFormat;
  }
  // ...
}
The call() method is then simple enough—I send the event request onto the call() method of the first link in the chain:
class CellFormatter {
  // ...
  void call(Event e) { first.call(e); }
  // ...
}
And, as mentioned, it is easy to manipulate the chain with methods like ignore() and obey(). A simple implementation for the number formatter could be:
class CellFormatter {
  // ...
  void ignore(String format) {
    if (format == 'number') first = _dateFormat;
    // ...
  }
  void obey(String format) {
    if (format == 'number') first = _numberFormat;
    // ...
  }
}
I feel more comfortable putting this knowledge in a class than I would in the main code. At least for this example, this seems like too much maintenance to mingle with other code.

This does lead to some significant improvements in the main code. Obviously, it is much simpler to instantiate one object instead of three (and to omit the linking):
  var cellFormat = new CellFormatter();
I also get the benefit of an easier to read (and hence easier to maintain) assignment of the cell-formatter:
  container.onChange.listen(cellFormat);
This one-liner reads nicely and has low cognitive requirements: when the spreadsheet container sees changes, it cell-formats the source elements. No worry about the chain or the first element in the chain. Formatting is done by the cell formatter. Simple!

(see last night's post for why cellFormat behave like a function here)

The nicest win in this approach may be in the code that ignores/obeys formatters. Previously, when ignoring the number formatter, I had to obtain a subscription from the initial listener so that I could then cancel it and start a new listener with the second link in the chain:

  var subscription= container.onChange.listen(numberFormat);

  query('#no-numbers').onChange.listen((e){
    var el = e.target;
    if (el.checked) {
      subscription.cancel();
      subscription = c.stream.listen(dateFormat);
    }
    // ...
  });
Now that CellFormatter internally maintains a reference to the first item in the chain, I no longer need to worry about the stream subscription sending to the wrong first element. The resulting code is mercifully free from subscription code, only obeying or ignoring links as requested:

  container.onChange.listen(cellFormat);

  query('#no-numbers').onChange.listen((e){
    var el = e.target;
    if (el.checked) {
      cellFormat.ignore('number');
    }
    // ...
  });
That seems a nice improvement.

Overall, I am very happy with the improvement in the code as a results of this approach. I have yet to examine it in depth, but I believe this is likely a facade pattern as it hides some of the underlying complexity of the chain of responsibility from the calling context. Regardless of what it is called, I very much like it!

This seems a nice stopping point for my research into the chain of responsibility. Up next, who knows?

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


Day #102

23 comments:

  1. Thank you for your article that is very useful and provides complete information for information seekers in the world.

    joker123
    agen joker123
    bandar joker123
    daftar joker123
    login joker123
    download joker123
    slot joker123

    ReplyDelete
  2. Nagaqq Yang Merupakan Agen Bandarq terbaik , Domino 99, Dan Bandar Poker Online Terpercaya di asia hadir untuk anda semua dengan permainan permainan menarik dan bonus menarik untuk anda semua

    Bonus yang diberikan NagaQQ :
    * Bonus rollingan 0.5%,setiap senin di bagikannya
    * Bonus Refferal 10% + 10%,seumur hidup
    * Bonus Jackpot, yang dapat anda dapatkan dengan mudah
    * Minimal Depo 15.000
    * Minimal WD 20.000
    * Deposit via Pulsa TELKOMSEL
    * 6 JENIS BANK ( BCA , BNI, BRI , MANDIRI , CIMB , DANAMON )

    Memegang Gelar atau title sebagai AGEN POKER ONLINE Terbaik di masanya

    Games Yang di Hadirkan NagaQQ :
    * Poker Online
    * BandarQ
    * Domino99
    * Bandar Poker
    * Bandar66
    * Sakong
    * Capsa Susun
    * AduQ
    * Perang Bacarrat
    * Perang Dadu (New Game)


    Info Lebih lanjut Kunjungi :
    Website : NAGAQQ
    Facebook : NagaQQ official
    WHATSAPP : +855977509035
    Line : Cs_nagaQQ
    TELEGRAM :+855967014811

    BACA JUGA BLOGSPORT KAMI YANG LAIN:
    Winner NagaQQ
    Daftar NagaQQ
    nagaqq

    ReplyDelete
  3. Sodium metabisulfite is an inorganic compound comprising sodium, sulfur, and oxygen. The sodium metabisulfite market can be segmented into food preservatives, wine making and distilleries, textiles, pulp & paper industry, chemicals and pharmaceuticals, and others.

    Also read: acetic anhydride market | Tobacco Market

    ReplyDelete
  4. Nice Blog.

    Global smart grid data analytics market will reach $4,664.2 million by 2027, growing by 11.8% annually over 2020-2027

    ReplyDelete
  5. Sebelum anda membaca lebih jauh lagi, terlebih dahulu lakukan pendaftaran dan mendapatkan akun ID Judi Slot Online untuk bermain dan mempraktikan permainan mesin slot yang akan kami berikan kepada anda hari ini.
    Coba buka juga tautan dibawah ini ya teman-teman :
    Situs Slot rjoker123.net
    Agen Judi Slot rjoker388.net
    Daftar Slot rjoker128.net
    Login Slot rjoker123.org

    ReplyDelete
  6. Situs ini sangat baik dan saya sangat suka membacanya. Meski begitu kami juga ada beberapa artikel terbaik buat kalian semua.

    Agen Poker 9Gaming
    9Gaming Poker

    Agen 9Gaming

    ReplyDelete