On Saturday, November 18, 2017 at 9:20:31 AM UTC-6, Edward K. Ream wrote: On Saturday, November 18, 2017 at 6:58:46 AM UTC-6, vitalije wrote: > > Your questions and suggestions are excellent, and show deep understanding > of the code. However, I think the present code, even with its flaws, is > slightly preferable. YMMV. >
I truly appreciate such questions. They challenge me to reconsider my normal way of thinking. *tl;dr:* Read the summary When I awoke this morning I had several addition thoughts. 1. The DRY principle can lead one astray by suggesting that we group statements together that have no other purpose than they *happen to be shared* in various, possibly unrelated, pieces of code. The great Glenford J. Meyers warned of this kind of code sharing in various writings, including his 1978 book, Composite/Structured design. Used copies of this book are still available for about $5. This was before any language had classes! Nowadays I suspect that most people learn design from studying existing classes, but I wonder how well the principles of designing classes are understood. Meyers advocated that all code have the strongest possible "binding strength". The top two he called "functional" and "informational" strength. The weakest form of binding is "coincidental" strength. An example is code that just happens to be shared in several places. Here are some quotes. In these quotes "module" could mean either a python module or class or function. Page 35: A *functional strength module* that performs a single specific function. Page 36: It is often the case that a program constructed with functional-strength modules still does not have a maximally independent structure...The *informational strength module*...[hides] some concept, data structure or resource within a single module. Imo, hiding details is the master design principle behind program design. *We want various classes to know as little as possible about all other classes.* All will be well when this is true. We must avoid cross-class dependencies whenever possible. So this is one reason why I am leery of having the plugins manager handle settings. It creates a cross-class dependency. 2. There is exactly one method that handles every Leo command. Each of these methods has full and complete responsibility for *all* aspects of the command's implementation. This responsibility can not really be delegated. In the present discussion, c.open and c.openRecentFile are the command handlers. *From a design standpoint, it really doesn't matter how complicated these methods are.* If we can devise *well designed* helpers that simplify the code then all the better. But those helpers must have functional/informational binding strength. And we don't want to pollute any other class with details best handled squarely by the command handlers. 3. The last reason I resist the notion of putting code into the plugins manager is that there arises the possibility that the plugins manager will call the settings-related code too often. I'm not sure what that possibility is, *and that's exactly the point*. I don't want to create any kind of hidden dependency or assumption between the plugins manager class and various Leo commands. *Far better*, imo, to duplicate the required code in c.open and c.openRecentFile. Even if initially I haven't covered all the bases, the proper solution is to deal with the complexity in whatever command handler is created or found. 4. Thinking about these issues suggests refactoring k.registerCommand into two pieces. The new k.registerCommand would reject, with a warning, any attempt to set a binding. This warning is necessary because we don't want to break existing code and we want to provide a clear migration path to the new code. At present, several pieces of code *legitimately* set the binding, including the mod_scripting plugin and the code that creates the Open With menu. The solution would be to have these call a *new* method, say k.setBindingForCommand. Details are fuzzy, but I think the refactoring makes sense. It will greatly simplify k.registerCommand in the typical usage. 5. The question about binding wrappers got me thinking about the IPython traitlets module, <https://github.com/ipython/traitlets> and the simplification it brings to their code. One result of this pattern is that few (no?) jupyter/ipython classes have ctors! Most ivars can be defined as class ivars. Leo is not going to use anything like traitlets, for several reasons. Most importantly, it would be too big a change in the code base for way too little gain. Still, it's good to think of what might have been... *Summary* Leo's design is sound, flexible and robust because: 1. Classes knows nothing about any other class. 2. There is a single *command method* for each Leo command, and that method is responsible for *all* implementation details of the command. Each command method knows *everything* about the implementation of the command, and *no other code anywhere knows anything about such details*. This is likely the clearest summary Leo's design that I have ever devised. Many thanks for the inspiration to create it. Edward -- You received this message because you are subscribed to the Google Groups "leo-editor" group. To unsubscribe from this group and stop receiving emails from it, send an email to leo-editor+unsubscr...@googlegroups.com. To post to this group, send email to leo-editor@googlegroups.com. Visit this group at https://groups.google.com/group/leo-editor. For more options, visit https://groups.google.com/d/optout.