Black Box Driven Development in JavaScript

Sooner or later every developer finds the beauty of the design patterns. Also, sooner or later the developer finds that most of the patterns are not applicable in their pure format. Very often we use variations. We change the well-known definitions to fit in our use cases. I know that we (the programmers) like buzzwords. Here is a new one – Black Box Driven Development or simply BBDD. I started applying the concept before a couple of months, and I could say that the results are promising. After finishing several projects, I started seeing the good practices and formed three principles.

What is a black box?

Before to go with the principles of the BBDD let’s see what is meant by a black box. According to Wikipedia:

In science and engineering, a black box is a device, system or object which can be viewed in terms of its input, output and transfer characteristics without any knowledge of its internal workings.

In programming, every piece of code that accepts input, performs actions and returns an output could be considered as a black box. In JavaScript, we could apply the concept easily by using a function. For example:

var Box = function(a, b) {
    var result = a + b;
    return result;
}

This is the simplest version of a BBDD unit. It is a box that performs an operation and returns output immediately. However, very often we need something else. We need continuous interaction with the box. This is another kind of box that I use to call living black box.

var Box = function(a, b) {
    var api = {
        calculate: function() {
            return a + b;
        }
    };
    return api;
}

We have an API containing all the public functions of the box. It is identical to the revealing module pattern. The most important characteristic of this pattern is that it brings encapsulation. We have a clear separation of the public and private objects.

Now that we know what a black box is, let’s check out the three principles of BBDD.

Principle 1: Modulize everything

Every piece of logic should exist as an independent module. In other words – a black box. In the beginning of the development cycle it is a little bit difficult to recognize these pieces. Spending too much time in architecting the application without having even a line of code may not produce good results. The approach that works involves coding. We should sketch the application and even make part of it. Once we have something we could start thinking about black boxing it. It is also much easier to jump into the code and make something without thinking if it is wrong or right. The key is to refactor the implementation till you feel that it is good enough.

Let’s take the following example:

$(document).ready(function() {
    if(window.localStorage) {
        var products = window.localStorage.getItem('products') || [], content = '';
        for(var i=0; i';
        }
        $('.content').html(content);
    } else {
        $('.error').css('display', 'block');
        $('.error').html('Error! Local storage is not supported.')
    }
});

We are getting an array called products from the local storage of the browser. If the browser does not support local storage, then we show a simple error message.

The code as it is, is fine, and it works. However, there are several responsibilities that are merged into a single function. The first optimization that we have to do is to form a good entry point of our code. Sending just a newly defined closure to the $(document).ready is not flexible. What if we want to delay the execution of our initial code or run it in a different way. The snippet above could be transformed to the following:

var App = function() {
    var api = {};
    api.init = function() {
        if(window.localStorage) {
            var products = window.localStorage.getItem('products') || [], content = '';
            for(var i=0; i';
            }
            $('.content').html(content);
        } else {
            $('.error').css('display', 'block');
            $('.error').html('Error! Local storage is not supported.');
        }
        return api;
    }
    return api;
}

var application = App();
$(document).ready(application.init);

Now, we have better control over the bootstrapping.

The source of our data at the moment is the local storage of the browser. However, we may need to fetch the products from a database or simply use a mock-up. It makes sense to extract this part of the code:

var Storage = function() {
    var api = {};
    api.exists = function() {
        return !!window && !!window.localStorage;
    };
    api.get = function() {
        return window.localStorage.getItem('products') || [];
    }
    return api;
}

We have two other operations that could form another box – setting HTML content and show an element. Let’s create a module that will handle the DOM interaction.

var DOM = function(selector) {
    var api = {}, el;
    var element = function() {
        if(!el) {
            el = $(selector);
            if(el.length == 0) {
                throw new Error('There is no element matching "' + selector + '".');
            }
        }
        return el;
    }
    api.content = function(html) {
        element().html(html);
        return api;
    }
    api.show = function() {
        element().css('display', 'block');
        return api;
    }
    return api;
}

The code is doing the same thing as in the first version. However, we have a test function element that checks if the passed selector matches anything in the DOM tree. We are also black boxing the jQuery element that makes our code much more flexible. Imagine that we decide to remove jQuery. The DOM operations are hidden in this module. It is worth nothing to edit it and start using vanilla JavaScript for example or some other library. If we stay with the old variant, we will probably go through the whole code base replacing code snippets.

Here is the transformed script. A new version that uses the modules that we’ve created above:

var App = function() {
    var api = {},
        storage = Storage(),
        c = DOM('.content'),
        e = DOM('.error');
    api.init = function() {
        if(storage.exists()) {
            var products = storage.get(), content = '';
            for(var i=0; i';
            }
            c.content(content);
        } else {
            e.content('Error! Local storage is not supported.').show();
        }
        return api;
    }
    return api;
}

Notice that we have separation of responsibilities. We have objects that play roles. It is easier and much more interesting to work with such codebase.

Principle 2: Expose only public methods

What makes the black box valuable is the fact that it hides the complexity. The programmer should expose only methods (or properties) that are needed. All the other functions that are used for internal processes should be private.

Let’s get the DOM module above:

var DOM = function(selector) {
    var api = {}, el;
    var element = function() { … }
    api.content = function(html) { … }
    api.show = function() { … }
    return api;
}

When a developer uses our class, he is interested in two things – changing the content and showing a DOM element. He should not think about validations or change CSS properties. In our example, there are private variable el and private function element. They are hidden from the outside world.

Principle 3: Use composition over inheritance

One of the popular ways to inherit classes in JavaScript uses the prototype chain. In the following snippet, we have class A that is inherited by class C:

function A(){};
A.prototype.someMethod = function(){};

function C(){};
C.prototype = new A();
C.prototype.constructor = C;

However, if we use the revealing module pattern, it makes sense to use composition. It is because we are dealing with objects and not functions (* in fact the functions in JavaScript are also objects). Let’s say that we have a box that implements the observer pattern, and we want to extend it.

var Observer = function() {
    var api = {}, listeners = {};
    api.on = function(event, handler) { … };
    api.off = function(event, handler) { … };
    api.dispatch = function(event) { … };
    return api;
}

var Logic = function() {
    var api = Observer();
    api.customMethod = function() { … };
    return api;
}

We get the required functionality by assigning an initial value to the api variable. We should notice that every class that uses this technique receives a brand new observer object so there is no way to produce collisions.

Summary

Black box driven development is a nice way to architect your applications. It provides encapsulation and flexibility. BBDD comes with a simple module definition that helps organizing big projects (and teams). I saw how several developers worked on the same project, and they all built their black boxes independently.

About Krasimir Tsonev

Krasimir Tsonev is a front-end developer, blogger and speaker. He loves writing JavaScript and experimenting with the latest CSS and HTML features. Author of the "Node.js blueprints" book he is focused on delivering cutting edge applications. Krasimir started his career as graphic designer, he spent several years coding in ActionScript3. Right now, with the rise of the mobile development, he is enthusiastic to work on responsive applications targeted to various devices. Living and working in Bulgaria he graduated at the Technical University of Varna with bachelor and master degree in computer science.

More articles by Krasimir Tsonev…

About Robert Nyman [Editor emeritus]

Technical Evangelist & Editor of Mozilla Hacks. Gives talks & blogs about HTML5, JavaScript & the Open Web. Robert is a strong believer in HTML5 and the Open Web and has been working since 1999 with Front End development for the web - in Sweden and in New York City. He regularly also blogs at http://robertnyman.com and loves to travel and meet people.

More articles by Robert Nyman [Editor emeritus]…


35 comments

  1. m

    Please don’t use this (anti)pattern below in examples:
    C.prototype = new A();
    Why preparing a prototype of one class should call constructor of another class (with all possible side-effects)?
    IMHO this (in year 2014) would be better:
    C.prototype = Object.create(A.prototype);

    August 27th, 2014 at 06:27

    1. Krasimir Tsonev

      Note taken. I’ll have this in mind.

      August 27th, 2014 at 07:35

    2. Adam Freidin

      And in the next line,…

      C.prototype.constructor = A;

      shouldn’t that be

      C.prototype.constructor = C;

      ?

      August 27th, 2014 at 07:46

      1. Krasimir Tsonev

        Hi Adam, you are right.
        @RobertNyman: can you please fix it.
        C.prototype.constructor = A;
        to
        C.prototype.constructor = C;

        August 27th, 2014 at 08:33

        1. Robert Nyman [Editor]

          Yep, changed it!

          August 27th, 2014 at 08:37

    3. Luke

      What does it mean to “create” a prototype of another class, for another class’s prototype? How is it better?

      The MDN article has some examples of the method used in this article, and it seems to be a clean and easy-to-understand pattern:
      https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype#Examples

      August 27th, 2014 at 20:27

  2. Pavel Ivanov

    Nice one :) good job guys :)

    August 27th, 2014 at 06:31

  3. Norman Kabir

    Excellent article. There’s a missing ‘l’ in “Principle 2”.

    August 27th, 2014 at 06:33

    1. Robert Nyman [Editor]

      Thanks! Fixed.

      August 27th, 2014 at 06:50

  4. Felix Hammerl

    Using a module pattern to create an instance is a nice way to hide private function, but there’s a downside to it. Attaching instance methods to a prototype is faster and more memory efficient, also that’s what the prototype system is for. In your case, every single instance has its very own function instead of every instance using the same method.

    In my experience, prefixing internally used methods with an underscore is good enough, as the underscore says: “Direct usage of this function is discouraged”. Should anyone break this rule, it will surface in code reviews. Readability is king and I don’t know if this actually easier to understand than this._foo(), as it is clearer what’s going on. Also, “new” is preferable to convenience constructors. There are frameworks for other languages out there that had convenience constructors, but later opined against it…

    August 27th, 2014 at 06:44

    1. Krasimir Tsonev

      What a great comment. Thanks Felix. I’m in endless debate with myself what exactly to use. The prototype-like architecture or the revealing module pattern. I still can’t decide to be honest.

      August 27th, 2014 at 09:02

  5. Ted k’

    Nice!!! excellent!!!

    August 27th, 2014 at 06:55

  6. Erick Mendoza

    Really great reading! This is something we should have permanently in our mindset as we develop any kind of JavaScript applications. I haven’t thought on how to apply the Principle #3, and now I see how convenient it is! Thank you.

    August 27th, 2014 at 07:36

    1. Krasimir Tsonev

      Hi Erick,
      I’m very glad that you find the article interesting. It is a concept that I’m using last few years. Finally I found some time to define and document it :)

      August 27th, 2014 at 08:35

  7. Dave

    Isn’t this approach basically what unit testing is about? What is the difference?

    August 27th, 2014 at 07:48

    1. Krasimir Tsonev

      Exactly! The thing that I call Black Box Driven Development encourage the writing of units. I’m using test driven development for several years and when I started I was writing integration tests. Not unit tests because I normally didn’t have units. However, once I started following the concept described in this article the things changed.

      August 27th, 2014 at 08:40

  8. Marcos Rodrigues

    Nice examples of refactoring, although I’d call it simply good object-oriented design.
    Some changes I’d suggest:
    – allow an argument for which key to fetch on the Storage class;
    – separate data-fetching, data-presentation, and view concerns.

    August 27th, 2014 at 08:24

    1. Krasimir Tsonev

      +1 for the suggestions. I really like the idea about the key to fetch on the local storage. The second one also brings some great ideas for improvements. I see a little bit of *speculative generality* (http://goo.gl/yth5bX) there but overall they are good suggestions.

      August 27th, 2014 at 08:58

      1. Marcos Rodrigues

        Thanks! Yeah, I thought that, regarding this simple example, these suggestions would be an overdesign, but since the principle proposed should be applicable to large-scale projects, these could be handy.

        Still, I think a data-presentation class could be used to improve readability even in this case. Here an idea:

        var products = storage.get();
        c.content(ProductsPresenter(products).list());

        August 27th, 2014 at 09:37

        1. Krasimir Tsonev

          I agree. It makes sense.

          August 27th, 2014 at 14:10

  9. Rene

    Nice article. :)

    But I would add a “addMethod” method to the api object instead of adding properties this way: api.customMethod = function() { … };

    August 27th, 2014 at 08:57

  10. azendal

    Black bloxing does not only help on coding, it also helps while creating specifications for your programs (before creating them) to create simple mental model of the program, its responsibilities and results. This can be used to run “test” on the modeling stage of the development.

    August 27th, 2014 at 09:49

  11. AutomateAllTheThings

    I’ve been advocating this style of development for the past 6 months, without ever having a good name for it! We were calling it “encapsulated design”, but now we much prefer your term.

    I can’t say enough about the benefits of this methodology. I think I’ll do a write-up about our setup, now that I know what to call it!

    August 27th, 2014 at 18:54

    1. Krasimir Tsonev

      I’m glad that you like the name :) I also spent few weeks wondering how to call it :)

      August 28th, 2014 at 03:49

  12. Pulak

    Very good article to read.

    The important point that I have understood from the article is modularising your codebase. Also, write your code in such a way that it is agnostic of any external library like jQuery.

    In “Principle 1” code example, why are you returning api from api.int, api.content, api.show functions when you are already returning api from the end of the function?

    August 27th, 2014 at 19:34

    1. Krasimir Tsonev

      In general, it is a good practice to return something (I mean every function). The more practical explanation would be that we can chain the methods like for example:
      .init().content().show()

      August 28th, 2014 at 03:52

  13. David Hanson

    We dont need BBDD…..we already have SOLID which is even better.

    August 27th, 2014 at 22:17

    1. Krasimir Tsonev

      +1 for mentioning SOLID. It is another approach that I like. In some cases I wasn’t able to apply all the principles but there are few great principles listed.

      August 28th, 2014 at 03:55

      1. David Hanson

        So interestingly, if you develop with TypeScript and Angular you can do full SOLID

        TypeScript gives your interfaces and Angular gives you dependency injection and IoC,

        August 28th, 2014 at 05:48

  14. TedvG

    Nice that you found out about modular principles and design :o) But more seriously: You can prevent all this hassle by using a modular object oriented language in the first place, as objects are modular and black boxes right away. E.g use Dart instead, it compiles also to (faster) Javascript. Javascript does not really lend itself to modular programming, it was never meant that way. Have fun

    August 27th, 2014 at 23:57

    1. Krasimir Tsonev

      Hi TedvG,
      thank you for the comment. I’ll definitely check Dart in the upcoming months.

      August 28th, 2014 at 03:56

  15. Matt Perdeck

    How is “Black Box Driven Development” different from object oriented programming?

    You might want to check out TypeScript. It makes creating object, inheritance, private methods, etc. much easier than pure JavaScript.

    August 28th, 2014 at 03:14

    1. Krasimir Tsonev

      Hi Matt,

      thanks for the comment. Actually in BBDD I used OOP. I mean I’m not trying to invent a new thing. I just defined my workflow with a new buzzword :)

      I’m not a big fan of TypeScript-liked languages. That’s because I’m trying to use as less abstractions as possible in every level of my development cycle. It is the same with the CSS preprocessors. I even create one but I don’t like to use them anymore. This is of course personal opinion and depends on a lot of factors.

      August 28th, 2014 at 06:38

  16. Acaz

    One more buzz word for a good object oriented code.

    August 28th, 2014 at 09:43

  17. Loops

    Before to use black box, you must keep in mind that : Does anybody, anytime, may need to change a little thing on the private stuff ? If yes, do not use black box.

    In prototype oriented programming, you can overwrote a prototype method to make all new instances using the new method. With black box, the prototype ability of the class is lost in space and time. In fact, it’s like saying: I will use my bike like a “trottinette”.

    September 4th, 2014 at 01:08

Comments are closed for this article.