Encapsulation

If you come from a more traditional programming language like C++, Java, or even PHP, you’re probably already familiar with the concept of encapsulation. So what is encapsulation? It’s the act of hiding some parts of your code from some other parts of your code, and only exposing what’s deemed necessary for the consumers of that code. Think about it as your custom object types providing an API, or interface, to any external code that might be consuming the functionality your custom objects provide. Here’s an example implementation:

var AddingMachine = function (displayElement) {
    this.displayElement = displayElement;
    this.runningTotal = 0;
}

AddingMachine.prototype = {
    add: function (operand) {
        this.runningTotal += operand;
        this._updateDisplay(this.displayElement, this.runningTotal);
    },
    subtract: function (operand) {
        this.runningTotal -= operand;
        this._updateDisplay(this.displayElement, this.runningTotal);
    },
    multiply: function (operand) {
        this.runningTotal *= operand;
        this._updateDisplay(this.displayElement, this.runningTotal);
    },
    divide: function (operand) {
        if (operand !== 0) { // Not in my universe, buddy
            this.runningTotal += operand;
            this._updateDisplay(this.displayElement, this.runningTotal);
        }
    },
    _updateDisplay: function (displayElement, runningTotal) {
        displayElement.innerHTML = runningTotal;
    }
};

var myAddingMachine = new AddingMachine(document.getElementById("adding-machine"));
myAddingMachine.add(5);
myAddingMachine.subtract(2);
myAddingMachine.multiply(4);
myAddingMachine.divide(6);

This is a simple enough example. In the end, we should have the number 2 displayed in the #adding-machine element we passed into our constructor. However, we also have direct access to our _updateDisplay() function, meaning that if we were so inclined, we could skip all of the calculations and jump straight to this:

myAddingMachine._updateDisplay(55378008);

Or even do away with numbers altogether:

myAddingmachine._updateDisplay("Ha ha! I'm an adding machine!");

We’ve prefixed that function name with an underscore, which is typically used to denote that something should be treated as protected or private as far as the interface is concerned. However, that doesn’t mean consumers will follow those guidelines. We could even directly access that function without creating an instance of our AddingMachine object by doing something like this:

AddingMachine.prototype._updateDisplay(document.getElementById("some-other-element"), "1 billion dollars!");

We really don’t want our consumers being able to reference this private function, so why expose it at all? Let’s make it truly private. In order to do that, we’ll need to wrap our custom object definition in a closure to hide its contents from any external code, and return only what we want to expose.

var AddingMachine = (function () {
    var AddingMachine = function (displayElement) {
        this.displayElement = displayElement;
        this.runningTotal = 0;
    }

    function updateDisplay (displayElement, runningTotal) {
        displayElement.innerHTML = runningTotal;
    }

    AddingMachine.prototype = {
        add: function (operand) {
            this.runningTotal += operand;
            updateDisplay(this.displayElement, this.runningTotal);
        },
        subtract: function (operand) {
            this.runningTotal -= operand;
            updateDisplay(this.displayElement, this.runningTotal);
        },
        multiply: function (operand) {
            this.runningTotal *= operand;
            updateDisplay(this.displayElement, this.runningTotal);
        },
        divide: function (operand) {
            if (operand !== 0) { // Not in my universe, buddy
                this.runningTotal += operand;
                updateDisplay(this.displayElement, this.runningTotal);
            }
        }
    };

    return AddingMachine;
}());

When this executes, what we’ll get is a nicely packaged custom object definition for an AddingMachine, complete with prototype, and no public access to the updateDisplay() function that it uses. The updateDisplay() function itself is now truly encapsulated in a private scope, unavailable to any consumers of this object type, except through the add, subtract, multiple, and divide methods that we’ve provided in its public interface.

Similarly, you can use a setup like this to ensure that you have a proper layer of separation from your object’s internal state properties, and use accessors and mutation methods to interact with your objects. And as far as those methods are concerned, always eat your own dog food when it comes to your implementations. This means that if you need to interact with your state properties, do so through your accessor and mutation methods. They allow you to perform sanity checks on the values, and to have a central implementation of that interaction logic, so that if it ever needs to be changed, you only need to change it in one place.

var AddingMachine = (function () {
    var runningTotal,
        displayElement,
        AddingMachine = function (displayElement) {
            setDisplayElement(displayElement);
            setRunningTotal(0);
        };

    function getRunningTotal () {
        return runningTotal;
    }

    function setRunningTotal (runningTotal) {
        if (!isNaN(runningTotal)) {
            runningTotal = runningTotal;
        }
    }

    function getDisplayElement () {
        return displayElement;
    }

    function setDisplayElement (displayElement) {
        displayElement = displayElement || null;
    }

    function updateDisplay (displayElement, runningTotal) {
        if (displayElement) {
            getDisplayElement().innerHTML = runningTotal;
        }
    }

    AddingMachine.prototype = {
        add: function (operand) {
            setRunningTotal(getRunningTotal() + operand);
            updateDisplay(getDisplayElement(), getRunningTotal());
        },
        subtract: function (operand) {
            setRunningTotal(getRunningTotal() - operand);
            updateDisplay(getDisplayElement(), getRunningTotal());
        },
        multiply: function (operand) {
            setRunningTotal(getRunningTotal() * operand);
            updateDisplay(getDisplayElement(), getRunningTotal());
        },
        divide: function (operand) {
            if (operand !== 0) { // Not in my universe, buddy
                setRunningTotal(getRunningTotal / operand);
                updateDisplay(getDisplayElement(), getRunningTotal());
            }
        }
    };

    return AddingMachine;
}());

We now have an implementation that completely hides our internal state variables from the external interface. After the initial construction of our AddingMachine object, there’s no way to reference either the internal displayElement or runningTotal values from that object instance. Even our own public-facing methods have private accessors and mutation methods for dealing with that state internally, just to give us a place to consolidate some business logic. All we can do is interact with it through the API we’ve made public, namely our add, subtract, multiple, and divide methods.

Hopefully, by now, you have an idea of how to structure your code in a way to prevent people from directly modifying the state of your objects. This isn’t always as important as it may sound, but it is important to know how to do for when you truly have the need to do it. As always, if you have any questions about anything related to this post, you can feel free to post them in the comment section below.

Leave a Reply

Your email address will not be published. Required fields are marked *