Whenever I am tempted to use an event, I ask myself if there a way to solve this problem with delegation or data-binding or some other solution.
I fall into the event trap all the time, but when I come across an event-free solution like Angular/Ember or using delegates in Obj-C, I am immediately sold.
Any pattern that promotes events as first class ways to communicate are a recipe for hell. Every Backbone application I have seen is nothing but a messy event hub that mangles arbitrary DOM events with arbitrary Model events, and there is a metric shit ton of code that just re-invents data-binding and delegation.
MVVM: model change listeners on each state mutation: response to state mutation is immediate, never batch multiple listeners, requires dependency tracking (if the collection changed, then the count also changed). change listeners fire immediately and can trigger more change listeners. Lots of callbacks to keep track of and easy for things to get out of whack. (Backbone, Knockout, Knockback)
Angular ("MV-whatever"): state changes detected by dirty checking, but changes are immediate (digest loop), never needs dependency tracking. Digest loop runs until the state settles (no more change listeners fire) to ensure consistent views. Magic data binding.
Facebook React: state w/ dirty checking: state changes are always batched, views are a pure function, reconciliation step to merge desired view with existing view, no events or change listeners. Render process is a lot like a 3D game - draw the buffer, whack the buffer, loop. No magic. Facebook doesn't call it MVC but in my eyes it is.
The more complex your view and state, the more events/mutations/callbacks, the more you need React.
> Mouse and key events are essential complexity.
I disagree, but probably not with what you mean here. Mouse and Key events are the first thing I want to abstract away, and are an example of the pitfalls of events. The majority of internet connected devices have neither a mouse nor keyboard. I don't want to care about key events or mouse movements, but rather user intent. "submit" and "change" describe user intent; I don't give a shit about clicks and keydowns, or blitting glyphs into an input box or moving the cursor on command-a. What does keydown mean when a Chinese person is swiping chinese characters?
I think of events as chemotherapy. It is one hell of a powerful tool, but should be avoided for most problems. If every problem looks like cancer, then your worldview is missing simpler and less powerful but better tools.
That's the DOM mutation side of it, anyway. I'm don't think I understand how the dirty check/batched event thing works for information going the other direction (user changed the value of a dropdown), and I'll abstain from guessing.
Personally, I don't mind events so much, so react's probably not the framework for me.
Components in React take a set of properties and implement a render function that transforms those properties into a view (usually by rendering other high-level React components rather outputting plain old HTML). Instead of manually maintaining view state you just render from scratch each time; hence the description of pure functions in parent comment.
Usually this would be a terrible idea because it would kill browser performance and lead to extra complexity in registering/unregistering event handlers, etc. but React implements a framework that ensures the minimum number of DOM interactions happen.
A better approach I think would to use FRP (functional reactive programming) on streams of events which eventually sends messages to an agent (one for each category of concern). This enforces a clean separation and allows for simple inspection and testing.
It also has the added benefit of allowing you to scale easily by putting the different agents on different machines, or even sending messages to one of several identical agents randomly.
The back story to that is that you need to put some effort into design. I've been using an event-based design as the basis for all native apps I've built since 2004, and it's evolved into something that's predictable and obvious - primarily because of separation of concerns.
For a simple example, here's a short description of the client I use for my blog: https://www.wittenburg.co.uk/Diagram.aspx?id=5919c0a5-dcb5-4...
The blog post describing the diagram is here: https://www.wittenburg.co.uk/Entry.aspx?id=5919c0a5-dcb5-4e5...
In this approach the model (I call them application entities) is the only part of the system that can raise application-wide events. These are limited to things that happen to the model, eg. Login; per collection, things like Selected, Added, Removed; and per entity, things like Saved, and Deleted.
The UI raises events (keyboard/mouse/etc), but their scope is limited to the UI. There's a service agent that abstracts asynchronous calls to a service, and it raises an event once an async call completes, but that is only visible to application components (Operations, in the linked post).
My specific implementation of this design is based on the architecture of Microsoft Office apps (the idea started formulating after working with Word's document object model).
It's clean and like I mentioned above, predictable.
Controller -> Application Objects -> Model -> View -> Controller
That way, the 'web page' controller can be replaced by a 'web service' controller or a 'command line' controller and the application logic remains untouched.
Spaghetti controllers are a result of not separating concerns.
I also agree with your event compromise. There are always going to be events that need to be broadcast systemwide (logout), but they should be limited.
I've been toying with using a message queue instead of events, it may work, it may not...
Lots of times people learn these lessons the hard way so I can learn them the easy way. For example, some people(s) noticed all the ways you can shoot yourself in the foot with pointer arithmetic and noticed that all these bugs aren't even directly related to the problems you're trying to solve. I, for one, am glad I develop in a high level language that abstracts pointers away from me. I learned assembly language, C and C++, but I don't miss it.
This isn't a dichotomy. Sure, good judgement and discipline are great, but abstractions that prevent bugs are great, too.
> Most high-level languages can be abused and transformed into abomination. No amount of syntactic sugar and type checking will serve you better than a handful of good judgement and a pinch of discipline, especially while you are architecting the core of your application in assembly.
Not untestable or arbitrary, or necessarily a problem. First, almost all processes have some structure that is not random and arbitrary. Second, the science and engineering of distributed systems has some results and approaches that help deal with events that are, or appear, arbitrary. For example idempotence, immutability, monotonically increasing functions such as max() that give the same result independent of the timing or order of events. It's not easy necessarily, but in a distributed system, events are what you have under the pile of turtles.
What a timeless reference.
I have to agree though, the AP is over reaching a fair bit with that statement, there are plenty of ways to implement message based systems that don't suffer from a god complex.
But isn't Angular full of magic?
MacOS apps, for instance, are built largely around Objective C, which borrows concepts from SmallTalk. They have delegates and other "we'll call you" concepts. Compare that with Windows apps in the past, where you had to write your own event loop and explicitly listen for events.
On the web, a library like Angular.js encourages you to put your code in one place and it "takes care of the rest". The "inversion of control" is once again very central to the whole thing. And in both cases, it makes for great declarative UI building (UI builder on the Mac, HTML on the web).
In short, if you have a framework, chances are it exposes BOTH kinds of things -- you call it to create / manipulate some objects, but it also calls YOUR code on important events.
If you don't care about mousedown/up events vs touchstart/end events - fine. All that can be abstracted into other events. But you need to respond to events -- whether user input or things coming in.
In our framework, Q, we actually strongly encourage the developer to update their views on events instead of "ajax success" callbacks. That's because Q is built to power SOCIAL apps, and when you're collaborating on a document, you can't predict when a new message will be pushed via a socket ... it may come from someone else posting it to the collaborative document!
So you can't just "avoid" events -- they have their place, especially in multi-user real time environments.
Add to that its all JS, and you're hosed. Good luck maintaining your selinium tests.
The jQuery route is also interesting, as it registers events as actual methods. So trying to call an event that doesn't exist will, again, throw an exception.
If C++/Java called events messages then it would reveal exactly how broken this vtable crap is as a replacement for actual 'messaging'.
Most of the GoF book is a giant anti-pattern for using a broken languages.
Software design patterns (at least, on the level where you would bother to write books describing them in terms of source code templates, rather than library code) are language-specific (or language-family-specific) workarounds for things that the language doesn't abstract well.
OTOH, recognizing them in existing languages is both how you write better code with the language you have and start to develop/recognize better languages.
Are you aware that the book also has quite a few Smalltalk examples?
Design Patterns books tend to miss the practice with contrived examples. Real code focuses on problems people actually have.
Also, use other languages.
For instance abusing categories is something I learned from rails, when I learned of objc_setAssociatedObject I started adding properties via categories (which you aren't supposed to be able to do via a category).
Edit: better link
That's definitely a downside of an events-driven model, but I sometimes/oftentimes find it difficult and unmaintainable to squeeze in delegate callbacks (in objective-c) in code, where a notification (event) would work great. Sometimes, when I would have to implement a cascading callback of 2 or 3 delegates, I choose notifications for the clearness it gives to the code.
I don't know about Backbone (never tried it, maybe I should), but if the events are clearly contained in each controller, and the observer properly removed when the controller is deallocated, it's not a big problem (here I'm referring to a language like objective-c).
"way to solve this problem with delegation or data-binding"
But this is even less testable. Inheritance makes unit testing a total hell. Because you don't care only for your object, but everything that came before it.
And this usually requires heavy mocking and/or some other tricks.
Delegation makes it easier, sure, I'm not so sure about data-binding