OOP is Not Your Hammer

As a young coder, the first design pattern I learned was inheritance.  This was, of course, my introduction to Object Oriented Programming (OOP). I was blown away by the simple idea that I could add or change an object’s behavior by overriding bits of it. I eventually went on to learn and use much more advanced Design Patterns by the Gang of Four, all of which expand upon simple inheritance. It seemed that inheritance and OOP were the answer to all design problems, whether known or yet-to-be-discovered.

When I started working in JavaScript back in 1996, I looked for ways to apply these familiar patterns. I soon learned that JavaScript presented three hurdles to inheritance-based design patterns:

  1. Lack of formal interfaces

  2. Faulty prototype chaining

  3. Lack of a super construct

These hurdles have been tackled by countless programmers, myself included. Unfortunately, throwing code at these issues only makes the ultimate goal (inheritance-based design patterns) more complicated than it should be.

They’ve also been tackled via proposed changes to the language. Some, such as Object.create(), have other great uses, but we’ve moved on from OOP. JavaScript has it’s own set of tools that we can apply to create advanced patterns.

The Problem

Let’s look at a simple ecommerce site. Let’s say we have a page that displays a product and has an order form with an “Add to cart” button, as well as a widget that allows the user to specify a quantity to purchase.

This app would be pretty simple to design. One approach would be to create three components:

  • A model that embodies a shopping cart item and has properties such as productId and quantity.
  • One or more views that display the product and the order form.
  • A controller that coordinates the transaction.

You probably recognize this as a typical MVC-like pattern. There are lots of variations on MVC and a never-ending list of implementations.

However, MVC is not the problem. Product Management is. They now want you to display a live shopping cart in the right-hand margin of the page. The live shopping cart must show a list of all of the items in the cart, including the one being added, as well as recalculate the shipping costs, taxes, and the grand total.

You could apply OOP here by creating a new controller that inherits from the original. The new extended controller could coordinate with a new shopping cart view, sending it the new item and the newly calculated numbers. However, there are several potential problems with this approach:

  • Complexity: the coordination required of the extended controller means it has more logic.
  • Testing: the job of isolating the extended controller is exacerbated by the increased number of tasks it must perform and the increased number of dependencies.
  • Embedded side effect: the updating of the live shopping cart is a side effect to the controllers main task, which is to coordinate the transaction.

If Product Management never asked for another feature, the impact of these problems would seem quite minor, but as you know, Product Management will never stop asking for new features. After a few iterations, the controller inheritance hierarchy will likely become a convoluted mess of overrides, conditionals, and code forks.

Side effects are an immediate red flag to me.  It has been drilled into my brain that an object, a module, or a function should do only one thing at a time. In our case, the controller should ensure that the user’s input (productId and quantity) gets delivered to the server. The coordination with a live, on-screen shopping cart view is a side effect of this task.

My brain immediately wants to delegate the shopping cart coordination to another component. This idea, as it turns out, leads to other potential solutions and has some interesting benefits:

  • Complexity: complexity is reduced by compartmentalizing into separate components.
  • Testing: fewer dependencies and fewer tasks per component means each component is easier to test.
  • No side effects!

Solution #1: Events

One of the most popular design patterns in JavaScript is the “publish-subscribe” or “event bus” pattern. Components ask to be notified of events (i.e. they “subscribe”) or they emit events (i.e. they “publish”). Events typically consist of “name-spaced” strings and an associated payload.

In our ecommerce example, our controller could publish an event such as “cart.add-item” that has product details as its payload.  Some other component, a shopping cart controller for instance, could subscribe to “cart.add-item” events and use them to manage the on-screen shopping cart.

The popularity of this pattern is pretty good proof that it works. We have successfully delegated the task of coordinating the on-screen shopping cart to a new component and have encapsulated the task of communication between the controllers to the publish-subscribe mechanism. However, the publish-subscribe pattern has some drawbacks, as well:

  • More dependencies: the introduction of the publish-subscribe mechanism has added another dependency to the components. Testing now requires that the publish-subscribe mechanism be mocked or stubbed.
  • Non-native abstraction: events are published by emitting strings (with payloads) into an opaque mechanism. Code quality tools, including linters, can’t ensure that events will go to the correct recipients, and debugging tools don’t trace calls from publishers to subscribers.

Solution #2: AOP

What is AOP? Aspect Oriented Programming (AOP) is a means to change the behavior of – or add behavior to – methods and functions (including constructors) non-invasively. The added behavior is called “advice” and can be added before, after, or around the function it advises.

AOP is particularly easy in JavaScript, since the advice is encapsulated in functions, and functions are first-class objects. You’ve probably already used AOP without knowing it. The following code is a very primitive form of AOP:

In this example, the replacement function, myAdvice, is advising the original method. This type of advice is called “around” advice since it completely surrounds and can add behavior around the original method. You can imagine forms of this in which we wish to only add advice before or after the original method.

In fact, you can easily create your own helper function to add before and after advice:

AOP has several applications. In functional languages such as JavaScript, one of these is non-invasive component interconnect. Let’s use these simple helper functions in our ecommerce example to show how this works.

First, let’s say that our controller has a method, saveItem, that responds when the user clicks the “Add to cart” button and posts the order details to the server. Let’s also assert that the on-screen shopping cart controller has a method, addItem, that receives product details. We could code the interaction between these two controllers with the following code:

What’s nice about this code is that it’s a bit more declarative, and it’s a bit more obvious what’s happening.

Let’s extend the scenario a bit. As we know, the call to the server will be asynchronous, so some time will have elapsed between the time the user clicks the “Add to cart” button and the server returns a result back.  During this time, Product Management would like to show a spinner on the on-screen shopping cart. (Of course!)

If the controller’s saveItem method returns a promise, we could use that to know when to turn off the spinner or remove the product if the server rejects the post:

This code is really easy to test.  onscreenCart has no dependencies on controller or on a publish-subscribe mechanism. controller has no dependencies on onscreenCart or the publish-subscribe mechanism, either. The advice function above could easily be tested as well by only stubbing onscreenCart!

More info

AOP has many other applications, and should be part of every Javascript developer’s toolkit.

Want to get started with AOP?  Check out these excellent AOP resources and implementations:

  • rhysbrettbowen

    I wrote about AOP (http://modernjavascript.blogspot.com/2013/06/aspect-oriented-programming-in.html) and even made it easy if you’re using Backbone (https://github.com/rhysbrettbowen/Backbone.Advice) but there can be issues when mixing this with regular inheritance as you can easily override a decorated function. I’m working on a solution to apply all aspects that are applied along the chain at the final inheritance instead of once per level (https://github.com/rhysbrettbowen/Backbone.AdviceFactory). I’ve found it’s good to keep a flat structure with the inheritance and compose your objects together as you need them rather than extending pre-built ones. I’ll be writing a blog post of my findings soon on http://rhysbrettbowen.com

    • http://unscriptable.com/ John Hann

      Nice work @rhysbrettbowen:disqus :)

      To avoid the i-didnt-mean-to-advise-all-ancestors problem, you could advise object instances or constructors, instead of prototypes. I haven’t thought about how to do this in Backbone, but we do it regularly in our projects.

      My initial try at some heuristics for picking which to advise:

      1. Advise prototypes when you want *all instances and all descendants* of a type to produce a side-effect.
      2. Advise constructors when you want *all instances but not descendants* of a type to produce a side-effect. In this case, the advised constructor could advise instances it creates.

      3. Advise instances when you want *only a select population* of a type to produce a side-effect.

      – John

      • rhysbrettbowen

        It can be hard to know what you want subclasses to inherit. I prefer not to add to instances as I like to know what an instance will do based on the constructor that created it – modifying an instance can cause huge headaches down the line. I think a factory that handles how things are put on an object is the best way as it is closer to the coders mental model of what they want to happen – at least that’s what we’re finding here.

        Anyway I’ll write up all my findings and how I think they should interact, but the tl;dr of it will be that we have to use a factory to hijack how the inheritance is handled to get what the user intends and try to keep a flat hierarchy of composed constructors rather than a deep one.

    • http://unscriptable.com/ John Hann

      Take a look at [meld](https://github.com/cujojs/meld) for some ideas. It advises methods (on prototype or instances), constructors, and plain functions. I believe that [dcl](https://github.com/uhop/dcl) does, as well.

  • Dan Stocker

    You’re saying hammers are bad because you can’t build a house with a hammer. That’s a straw man. Most of us carry more than just a hammer on our belt. Also, who subclasses a controller every time there’s a feature request from management? Smells like another straw man. The expression you’re looking for is “bad design”. Whether or not it has anything to do with OOP, MVC, tight coupling, etc.
    Only after a de-tour to events, that are also dismissed, does it turn out that this whole article is a pitch for AOP. Why didn’t you put that in the title? I do believe you have something important to say here, but instead of referring to “less dependencies”, and “bit more declarative”, you could have went for an argument saying, events don’t guarantee execution order, AOP does! That would have been powerful. Instead, you went out of your way to bury the lead.

    • http://unscriptable.com/ John Hann

      Good points, @0743b8279738b2d2d15bbdd715b64f8d:disqus. Word limit is partly to blame here, but the rest is mine. Thanks for the feedback.

      Execution order is a great advantage that I had forgotten about. Very important. Thanks again.

  • Micah Smith

    Have you ever done an implementation with a “typed event” (is a “typed event” a mediator…?) to get rid of the event-string issue? I feel like that would be less code than AOP and give you more guarantees than the classic JS event stuff. Any thoughts?

    In general this is interesting… I’ve always only leaned on AOP for more utility-esque things like logging/caching. I never had thought about using it for more domain-level uses. Thanks for sharing

    • http://unscriptable.com/ John Hann

      Do you have a link where I can learn more about “typed events”? Sounds interesting.

      After posting this article, I remembered that my biggest complaint about events is *where* you can use them. You can raise an event from *anywhere* in code. This means you inevitably create execution paths that can only be understood by reading every line of code. In a large enough app, it’s impossible to know every line of code.

      When using AOP as the communication mechanism, on the other hand, the connection points can only sit at the surface — the interfaces — of the objects. It’s much easier to understand all of the interfaces than all of the source code, imho.

      Also, since I like to add advice in central locations (in application composition layers or main controllers, for instance), the execution paths can be understood by looking at only a handful of files. I like that. :)

  • Jeff Greenberg

    If isolation and testability are desired in the event-based approach, why not simply move the event generation and handling code out of the object instances and into some kind of mediator? This way the object instances would still be completely clean for testing and the code to wire up the events would be separated out as in your final AOP examples.

    As well, your AOP code looks good for this kind of focused application requirement, but throw in more code that needs to execute in a single piece of advice and I think the event-based code would likely be cleaner and easier to follow.

    Another point to consider is that AOP is typically used for cross-cutting concerns such as logging or security. Mixing these together with application logic could prove to be messy in many situations.

    Custom pub-sub systems do indeed introduce an abstraction on top of vanilla JavaScript that can lead to debugging issues, but I’ve never had much trouble in practice.

    • http://unscriptable.com/ John Hann

      Hey Jeff,

      I can’t envision how you can move the event generation out of the objects. That part isn’t obvious to me.

      > …I think the event-based code would likely be cleaner and easier to follow

      I tend to disagree. Events can be raised from anywhere in code. It sucks to have to know every line of code to know the execution flow of an app. AOP works at the interfaces and is often applied in a central location. This makes it much easier to understand the execution flow.

      > …AOP is typically used for cross-cutting concerns such as logging or security. Mixing these together with application logic could prove to be messy in many situations.

      In JavaScript, there are so many more applications for AOP than cross-cutting concerns. Why limit yourself?

      Also: mixing logging or security with application logic is just silly. AOP doesn’t conflate these any more or less than events, OOP, or any other pattern.

      Regards,

      – John

      • Jeff Greenberg

        First, I actually like your AOP examples and explanations and agree with much if not all of it in principle. I was more playing Devil’s Advocate and should have made it clear that I was doing so. In any case…

        1) On moving events out of objects:

        My point was that if you can use an ‘after’ method in a central place to wrap existing functions, you could use an alternate method in a central place to add pub/sub to an existing method or set of methods. That’s all. And my question of “why not” was perhaps not as rhetorical as I my writing made it seem to be. I really was asking what your viewpoint was, but didn’t make my question or my talking points clear. My apologies. The closest example I could dig up quickly of a library that would work in this scenario is http://radio.uxder.com/documentation.html , but I’m sure there are others.

        2) On code clarity:

        Yes, AOP does make execution flow clear and that is certainly one of its strong points. And yes events can be raised anywhere in code, but they don’t have to be. Even without the approach I mention in point #1 events don’t *have* to be used willy-nilly. I’ll point out as well that there’s nothing stopping anyone from adding advice willy-nilly in many different modules, objects, etc. As well, there are some systems where events flying around (even asynchronously) are the best model for the system. Your point about native debugging is well taken though.

        3) On mixing cross-cutting and application logic:

        I wasn’t attempting to suggest we *limit* ourselves to only using AOP for cross-cutting concerns such as logging, just pointing out what is a useful and widespread use for AOP that you didn’t mention explicitly in your post. I know it wasn’t your main point and should have noted it more as an ancillary comment.

        As far as the “silliness” of mixing together logging or similar with application logic, well… yes, you’re right. I assumed that if one of your goals for using AOP was traceability that you’d want to have all advice for a single target together in one “sequence” in one place. It is of course obvious that you can have a separate module or set of methods for applying non-application advice. So, yes, agreed– silly. I am curious, though, about what the different performance trade-offs are for lots of events vs. somewhat deeply nested advice application.

        Anyway, great post.

        Thanks.

        -Jeff

        • http://unscriptable.com/ John Hann

          Thanks for the thoughtful reply, Jeff!

          I’d like to get a better understanding of the performance of various event or AOP implementations, too. I suspect that the well-written ones are very fast: they’re basically just a tight loop over subscribers or advices.

          Hm… async event implementations add a small delay (setTimeout(f, 0), etc.) as a trade-off for developer sanity. (Events should call async for the same reason Promises do.) I suspect most event implementations, like backbone’s, are sync purely for performance reasons.

  • http://justkidding.com tqwhite

    I devised a protocol around this a few years ago when I found myself in event driven spaghetti hell on a big project. It was the first large JS implementation I had done and the UI was very, very complex. I started with events and, in no time, any screwup became impossible to fix, or at least, very time consuming.

    I started causing controllers to exchange communication handlers when one instantiated the other. That worked great. I devised a default taxonomy of actions and added situation specific ones as needed. It made it so that I could figure out exactly who was being communicated with. It was great.

    Then I added a standard method for ‘registering’, as I called it back then, handler functions, what you would call ‘advisors’. There were a few standard flavors of those and, of course, some situation specific ones. Compared to my other technique, this is more flexible but, it also means that I have to analyze things to find where to put a breakpoint.

    All of which is to say, Good post. I didn’t know the phrase ‘Aspect Oriented Programming’ but I encourage people to pay attention to it. It’s a solid technique and very helpful.

    • http://unscriptable.com/ John Hann

      Ahead of your time, man. ;)

  • Guest

    I am relatively new to advanced Javascript, and trying to figure something out –

    Where is the “Advising” code actually written? In your example (with the controller.saveItem and the onScreenCart) it seems that if this code is inside the controller module than the controller has to know about the onScreenCart, and vice versa.

    In the other hand when using events – each component (pub or sub) needs to know only the event string, not the other components.

    So where should the “controller.saveItem = after(controller.saveItem, function (promise)….” code be written? In the controller module or the onScrennCart module? And how does the AOP solves the coupling issue?

    Thanks!

  • lyosef

    I am relatively new to advanced Javascript, and trying to figure something out -

    Where is the “Advising” code actually written? In your example (with the controller.saveItem and the onScreenCart) it seems that if this code is inside the controller module than the controller has to know about the onScreenCart, and vice versa.

    In the other hand when using events – each component (pub or sub) needs to know only the event string, not the other components.

    So where should the “controller.saveItem = after(controller.saveItem, function (promise)….” code be written? In the controller module or the onScrennCart module? And how does the AOP solves the coupling issue?

    • http://unscriptable.com/ John Hann

      Good point about code location. The advising code can be written in any number of places. It’s just a function. If it’s a piece of a larger group of common behavior, it may make sense to make it a method of some third object. It’s also perfectly valid to have it be just a stand-alone function.

      In the code I’ve shown in the example, the advising code is not inside the controller. It is added to the controller’s saveItem method at run time. I probably should have written it like this:

      controller.saveItem = after(controller.saveItem, advisingFunction);

      And then defined advisingFunction. That might make it more clear.

  • brianm101

    Always amazes me how techniques that have been used for years and as obvious as how to spread butter are given a fancy name such as Aspect Orientated Programming ( Whatever that means!) and made out to be something new and fantastic

    Guess its like the clergy of years ago using Latin to make themselves appear smarter than everyone else….!

  • Mario T. Lanza

    I have been using AOP extensively in JavaScript for years (all you need is _.wrap). On the whole, I like it. It allows for concerns to be neatly separated since a module can simply layer advice onto existing prototype definitions. In this way AOP allows for modularity.

    The drawback is this: modules should be as easy to remove as they are to add in. Unfortunately, aspects are not easy to remove once you’ve layered on several pieces of advice from several modules. Aspects are applied in a Russian Doll fashion where each aspect wraps the ones that came before. When it comes time to pull out an aspect that is no longer needed, it cannot be undone cleanly because of the nesting.

    There are better approaches; however, JavaScript does not support them. The better approach would be to allow for multiple inheritance via a simply model. The simplest way I can explain it is imagine if Javascript instead of having a single prototype property, had a prototypes array. In this way, a module (e.g. a mixin) would simply be pushed onto that array. Numerous mixins could be. At a later point, when a mixin become unnecessary (as it may in a long running application), you simply remove the given mixin from the prototypes array. If this were allowed, there would never be any reason to copy methods via _.extend (this would reduce a lot of overhead). It could also allow for super; it’s just a matter of implementation.

    Modularity is great. AOP is nice too. Trouble is undoing what’s been done. The best modularity does not `cons` in russian-doll style. It uses a construct like an array from which items can be removed as easy as they are added. This philosophy is in direct support of DCI.

    (For what it’s worth. Allowing for prototypes instead of a single prototype would be easy enough to implement in a backwards compatible way. By default ‘prototype’ would simply refer to the head of the prototypes array.)

    • http://unscriptable.com/ John Hann

      Some AOP libraries, meld.js for instance, allow adding and *removing* advice. It sounds like _.wrap is a bit limited.

      meld.js: https://github.com/cujojs/meld/

      I’ve done many apps with prototype mixins. It’s always a disaster once you add more than one mixin, imho. Some mixin overrides have to come before the original method, some after. Then there are name clashes. It’s never clean.

      To be fair, you can get into insane situations no matter what technique you’re using. I find that I am able to wrangle a complex set of behavior much better with AOP than multiple inheritance.

      I agree that _.extend is very limiting. Have you looked at dojo’s multiple inheritance or dcl’s?

      • Mario T. Lanza

        I don’t consider multiple inheritance any more complicated than inheritance chains. Personally, I think inheritance can be dropped altogether. In Ruby, there is a style of programming where everything is a mixin. Take a look at most anything by John Nunemaker (i.e. Toystore) to see this style exemplified. IMO, this is the way to write modular code without aspects and also by relying on the `super` method.

        Will be looking into meldjs soon. I already use curljs, so that should be a good fit. Will also look into dojo declare. Thanks for the tips.

        • briancavalier

          Mario: re meld’s remove(). Around advice acts like onion layers, and because of that each around advice can only “see” and affect the next layer in, via the joinpoint that was provided to the around advice. As long as you don’t make assumptions beyond the layer you can see (which is an entirely reasonable constraint when using any compositional technique), around advice can be removed safely. We do it without problems in wire.js a good bit.

          Is it possible to create situations where yanking an around advice out from the middle of the onion causes problems? Of course it is. But then, it’s possible to create problematic situations using *any* composition technique, including inheritance, delegation, Ruby’s modules, Javascript prototypes, C++ virtual methods, etc, etc.

          I think the moral is at least two-fold: 1) Being aware of, and knowing when to apply a variety of techniques, including AOP, OOP, etc. is a good thing, and 2) every technique has gotchas, and knowing those is equally important.

          I’m looking forward to seeing the write-up on your “prototypes array” idea. Hopefully, I’ll learn another useful technique!

  • http://agilelikeacat.com/ Dave Conlin

    This was really interesting, thanks! I have to admit, I got pretty frightened at

    “The popularity of this pattern is pretty good proof that it works”

    But things definitely picked up from there.

    I would agree with Dan Stocker (above) though: it definitely feels like OO inheritance is a straw man here. The OO pattern I’d really like to see a good comparison to is the decorator pattern.

    When would you decorate the entire object (ie compose it with another object which defers to your original object for unchanged functionality and overrides changed functionality), and when just override the method in question?

    I guess the answer is something as boring as “when you want to override chunks of functionality, rather than one method”, but it does seem like a more valuable comparison than to inheritance.

    • http://unscriptable.com/ John Hann

      Ah ha, yes! The decorator pattern can be very similar, depending on the implementation and the application — especially since all JavaScript happens at run-time*. I just realized how similar these patterns are last week when I wanted to use AOP from within a decorator function. I wasn’t using an OOP decorator pattern, I was using a functional decorator pattern. However, it got me wondering why I choose one over the other.

      I think it comes down to API, for me. If I expect to use the new behavior as a group — and use it often — I would probably choose a decorator. That seems like a convenient way to consume the added behavior.

      On the other hand, if the behavior might be useful in pieces or might be applied to only certain instances, I would likely pick AOP.

      I guarantee I will have a different opinion in the future. The point is to have lots of tools. :)

      *unless you’re transpiling or doing other crazy shiz at build time

  • http://unscriptable.com/ John Hann

    We just added the first in a series of tutorials on AOP to know.cujojs.com: Intro to Aspect Oriented Programming | know cujoJS http://know.cujojs.com/tutorials/aop/intro-to-aspect-oriented-programming

  • pbouzakis

    GOFs design pattern really did not leave me the impression that inheritance is the backbone of OOP. Quite the opposite in fact. Composition is favored over inheritance. I’m also not sure what you mean by because Product Management is going to constantly change requirements that OOP doesn’t fit. I don’t see the correlation here. Goes to design, which you could use OOP principles to achieve your goals or not. Context is king as always.