Hacker News new | comments | show | ask | jobs | submit login

Events are an anti-pattern in my opinion. They allow the ultimate flexibility, but in essence are worse than global variables. With events, it is very hard to reason about behavior. Objects respond to messages from "god" at arbitrary times in an untestable way.

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.

Model change listeners are the anti-pattern, not events. Mouse and key events are essential complexity.


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.

You seem to know what you are talking about, but I just don't understand what you are saying. Can you explain the React thing a bit better?

> 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.

I think the idea is that instead of having a bunch of DOM elements waiting around for individual events that tell them to change state ("your text should now be this"), you get a whole rendered view ("here's what the DOM looks like now"), which is then merged with the current one. That cuts out of a lot of the weird interconnected events that fly around everywhere in MVVM, because the DOM is mostly just an output.

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.

React probably closer to being just the View than MVC or any other paradigm. It doesn't even have any model layer.

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.

For anyone wondering how Chinese/Japanese/Korean input works interms of DOM events check out the W3C's working draft of the IME API [http://www.w3.org/TR/2013/WD-ime-api-20130815/]. Specifically the sequence diagram near the end of the page.

Technically speaking it's the Google proposal, Mozilla is working on a counter proposal at https://wiki.mozilla.org/WebAPI/KeboardIME.

I get the feeling someone has been listening to the Elm language talk?


What's wrong with model change listeners?

I don't think events themselves are the issue so much as mutability. When you combine them you end up with changes that ripple through the entire program, making everything incredibly difficult to reason about.

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.

Can't comment about FRP, but can tell you that events can ripple through an entire system, but don't have to.

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.

That's similar to how I'm tending towards. Controllers shouldn't contain the actual business logic, that's for the application layer.

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...

Most patterns can be abused and transformed into abomination. No amount of data-binding or delegation 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.

Even if you're right, this isn't really useful because what you're saying is not actionable. I don't have a "use discipline" switch I can just turn on. Learning discipline comes from doing things the wrong way and being reflective about it.

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.

I could read this as:

> 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.

> "Objects respond to messages from "god" at arbitrary times in an untestable way."

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.

> 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.

"Objects respond to messages from "god" at arbitrary times in an untestable way."

But isn't Angular full of magic?

Perhaps "pure" events are a recipe for spaghetti code, the same way a low-level anything is a recipe for that. But if you have a framework built on top of events, then it's very maintainable.

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.

Not to mention people like to "refactor" event names, without telling other teams that hey, all the events you listen to are no longer going to fire.

Add to that its all JS, and you're hosed. Good luck maintaining your selinium tests.

This is the fault of your implementation, not the pattern. Event types (names) should be enums, not arbitrary strings. This makes it very easy to change the way the code reads if the meaning of the event slightly changes, and equally easy to obfuscate the event key in a production build.

You would need a static js compiler to type check the enum. So that's an extra step. This doesn't happen in Java or C, but browser based event models (the topic of the post) are almost always strings. Most front end shops choose this implementation. You could add that step to your build, sure, but in. Lot of cases the app is already interpreted so there is no "build" per se. Who else has seen this?

You can make them properties on a global object, so that using an invalid event name is at least a runtime error (when you try to bind to an undefined event) rather than being mystified about why the event never triggers.

You still need a runtime test to find the error though, which I why manual QA is required.

So code an eventing system that throws exceptions when an event name that isn't registered is used... It's not difficult; it's what I use in all my JavaScript.

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.

Exactly. Obj-C/Smalltalk have events built in because objects can actually receive 'messages'... can't respond to it? Send it somewhere else, rewrite it, etc.

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.

> Most of the GoF book is a giant anti-pattern for using a broken language.

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.

> Most of the GoF book is a giant anti-pattern for using a broken languages.

Are you aware that the book also has quite a few Smalltalk examples?

can you recommend a good design patterns book that you prefer? As someone who works with obj-c I'd be interested to hear your suggestion.

Reading the code of frameworks / projects you like working with. It's especially interesting to look at older versions and see how it has progressed.

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).

Kent Beck’s Smalltalk Best Practice Patterns is a great book. In Smalltalk, but it works rather well for good OO languages, in general. Very easy to read. http://amzn.com/B00BBDLIME?tag=hboon-20

I've heard very good things about this book: "A Mentoring Course on Smalltalk" [1]

[1] http://www.amazon.com/A-Mentoring-Course-on-Smalltalk/dp/B00...

Edit: better link

I haven't used it with Objective-C, but you should check out Smalltalk Best Practice Patterns. The two languages are close enough that it should work, Rubyists refer to it all the time.

I'm glad this was the top post. I don't think events are an anti-pattern, but they are way the hell overused. Part of the overuse is using (name something)MQ for everything, not just for centralized persistent async and pubsub in large systems. Part of it is because of functional languages making a comeback in the past several years, which then settled down again after a spike; They don't require being event driven of course, but stateless systems are the gateway drug to overly event-driven. And part of it is JavaScript's fault- if you were in JS in the bad old days of early Ajax, you knows what I mean, and JQuery just made it easier- but promises hide the fact now that so much is async, though I think promises are just as bad if not worse because they hide a lot of nonsense.

Are Angular and Ember really event free though? Aren't they just hiding the fact that they use events internally away from you?

Hiding? Event bindings are quite explicit, however their declarative nature makes them especially simple to manage.

nope you still need to register custom events one way or another, especially when using third party plugins or scripts in directives. You cant magically hookup a 3rd party widget in angular , you need to write event callbacks. Angulars has it's own (scripted) event loop on the top of the DOM event loop. this comes with a huge performance cost no ones wants to acknowledge.

> Objects respond to messages from "god" at arbitrary times in an untestable way

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).

"Objects respond to messages from "god" at arbitrary times in an untestable way."

"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

Objective-C/Cocoa has events via notifications and they are used pretty heavily.

Events are effectively delegates

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact