19

Ok, I know the title of this question is almost identical to When should I use event based programming? but the answers of said question have not helped me in deciding whether I should use events in the particular case I'm facing.

I'm developing a small application. It's a simple app, and for the most part its functionality is basic CRUD.

Upon certain events (when modifying certain data) the application must write a local copy of said data in a file. I'm not sure about what's the best way to implement this. I can:

  • Fire events when the data is modified and bind a response (generate the file) to such events. Alternatively, implement the observer pattern. That seems like unnecessary complexity.
  • Call the file-generating code directly from the code that modifies the data. Much simpler, but it seems wrong that the dependency should be this way, that is, it seems wrong that the core functionality of the app (code that modifies data) should be coupled to that extra perk (code that generates a backup file). I know, however, that this app will not evolve to a point at which that coupling poses a problem.

What's the best approach in this case?

6
  • 2
    I would say don't implement anything yourself - simply use an existing event bus. This will make life much simpler... Mar 13, 2016 at 7:29
  • Event driven programming is inherently asynchronous. Events may or may not come through, in the order you intended or maybe in another order or not at all. If you can deal with that extra complexity, go for it.
    – Pieter B
    Mar 13, 2016 at 12:18
  • Event-driven typically mean that your code is provided as call-backs and these are invoked from elsewhere in ways you cannot predict. Your description more sounds like that when something specific happens in your code you need to do more than a naive implementation would require. Just code the extra calls in. Mar 13, 2016 at 14:17
  • There is a difference between event-driven and event-based. See for example the highly thought-provoking .NET Rocks podcast episode 355 with Ted Faison, Ted Faison Takes Events to the Limit! (direct download URL) and the book Event-based programming: taking events to the limit. Mar 13, 2016 at 15:51
  • The interview with Ted Faison starts at 13 min 10 secs. Mar 13, 2016 at 16:12

4 Answers 4

31

Follow the KISS principle: Keep It Simple, Stupid, or the YAGNI principle: You Ain't Going to Need It.

You can write the code like:

void updateSpecialData() {
    // do the update.
    backupData();
}

Or you can write code like:

void updateSpecialData() {
     // do the update.
     emit SpecialDataUpdated();
}

void SpecialDataUpdatedHandler() {
     backupData();
}

void configureEventHandlers() {
     connect(SpecialDataUpdate, SpecialDataUpdatedHandler);
}

In the absence of a compelling reason to do otherwise, follow the simpler route. Techniques like event handling are powerful, but they increase the complexity of your code. It requires more code to get working, and it makes what happens in your code harder to follow.

Events are very critical in the right situation (imagine trying to do UI programming without events!) But don't use them when you can KISS or YAGNI instead.

1
  • I specially like the fact that you mentioned that firing events when data is changed is not trivial.
    – NoChance
    Mar 12, 2016 at 23:20
13

The example you describe of a simple data, where the modification triggers some effect can perfectly be implemented with the observer design pattern:

  • this is simpler to implement and maintain than full event driven code.
  • the coupling between subject and observer can be abstract, which facilitates separation of concerns.
  • it's ideal for one to many relation (subjects have one or many observers).

Event driven approach is worth its investment for more complex scenarios, when many different interactions may occur, in a many-to-many context, or if chain reactions are envisaged (e.g. a subject informs an observer, which in some case wants to modify the subject or other subjects)

2
  • 1
    I'm confused, isn't observer just one way to implement events?
    – svick
    Mar 13, 2016 at 15:17
  • 1
    @svick I don't think so. In event-driven programming you have a main loop that processes events in a many to many relationship, with decoupled senders and observers. I think the observer can contribute by processing a perticular type of event, but you can't achieve the full spectrum of EDP only with an observer. I think the confusion comes from the fact that in event driven software, observers are sometimes implemented on the top of event processing (typically MVC with a GUI)
    – Christophe
    Mar 13, 2016 at 15:56
5

As you say, events are a great tool to reduce coupling between classes; so while it can involve writing additional code in some languages without built-in support for events, it reduces the complexity of the big picture.

Events are arguably one of the most important tools in OO (According to Alan Kay - Objects communicate by sending and receiving messages). If you use a language which has built-in support for events, or treats functions as first-class citizens, then using them is a no-brainer.

Even in languages without built-in support, the amount of boilerplate for something like the Observer pattern is fairly minimal. You might be able to find a decent generic eventing library somewhere which you can use in all of your applications to minimise boilerplate. (A generic event aggregator or event mediator is useful in almost any kind of application).

Is it worthwhile in a small application? I would say definitely yes.

  • Keeping classes decoupled from each other keeps your class dependency graph clean.
  • Classes without any concrete dependencies can be tested in isolation without consideration for other classes in the tests.
  • Classes without any concrete dependencies require fewer unit tests for complete coverage.

If you're thinking "Oh but it's really only a very small application, it doesn't really matter that much", consider:

  • Small applications sometimes end up being combined with larger applications later on.
  • Small applications are likely to include at least some logic or components which may later need to be reused in other applications.
  • Requirements for small applications can change, prompting the need to refactor, which is easier when existing code is decoupled.
  • Additional features can be added later, prompting the need to extend existing code, which is also much easier when the existing code is already decoupled.
  • Loosely coupled code generally does not take much longer to write than tightly coupled code; but tightly coupled code takes a lot longer to refactor and test than loosely coupled code.

Overall, the size of an application should not be a deciding factor in whether to keep classes loosely coupled; SOLID principles aren't just for big applications, they applicable to software and codebases at any scale.

In fact, the time saved in unit testing your loosely-coupled classes in isolation should counter-balance any additional time spent decoupling those classes.

1
2

The observer pattern can be implemented in a much more smaller fashion than the Wikipedia article (or the GOF book) describes it, assumed your programming languages supports something like "callbacks" or "delegates". Just pass a callback method into your CRUD code (the observer method, which might be either a generic "write-to-file" method, or an empty one). Instead of "event firing" just call that callback.

The resulting code will be only minimal more complex than calling the file-generating code directly, but without the drawbacks of tight coupling of unrelated components.

That will bring you "best of both worlds", without sacrificing decoupling for "YAGNI".

11
  • That works the first time Doc, but when the event needs to trigger a second thing, it's likely the class would need to be modified doing it this way. If you use an observer pattern, you could add as many new behaviors as you wanted without opening the original class back up for modification.
    – RubberDuck
    Mar 13, 2016 at 11:00
  • 2
    @RubberDuck: the OP was looking for a solution "avoiding unnecessary complexity" - if there are different events / different behaviours needed, he probably would not consider the observer pattern as too complex. So I agree to what you said, when things get more complex, a full observer would serve him better, but only then.
    – Doc Brown
    Mar 13, 2016 at 11:05
  • A fair statement, but it feels like a broken window to me.
    – RubberDuck
    Mar 13, 2016 at 11:08
  • 2
    @RubberDuck: adding a full observer, with publisher/subscriber mechanics "just in case", when it is not really needed, feels to me like overengineering - that is not better.
    – Doc Brown
    Mar 13, 2016 at 11:16
  • 1
    I don't disagree that it can be over engineering. I probably feel the way I do because it's trivial to implement in my stack of choice. Anyway, we'll just agree to disagree?
    – RubberDuck
    Mar 13, 2016 at 11:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.