Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Composition Is King

DZone's Guide to

Composition Is King

Aggregation or composition? ... An age-old object-oriented programming question. Look here for some resolution.

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

The JavaScript community is becoming flooded with articles pushing to move toward functional programming or at least more toward composition over inheritance. For a long time, we’ve tried to standardize inheritance without the huge mess that comes with the verbosity of the prototype syntax, and now that we have a standard class keyword in ES2015, people are trying harder than ever to tell us we don’t need it. Those people are, for the most part, correct.

Of course, they can’t be wrong. The source of truth for all Object-Oriented programmers is Design Patterns: Elements of Reusable Object-Oriented Software by the “Gang of Four” which says to prefer composition over inheritance. It seems like most people don’t understand that though. They’re taught about inheritance and then try to do everything with that, but that’s not nearly as powerful or scalable.

Back to JavaScript, which can take lessons from the design patterns book, but it’s a much different language than the one that that book was written for. Besides using prototypes rather than true classes, it is also filled with many bits of functional programming features. I’m not going to say don’t use the new class keyword or inheritance or anything like that. I just want you to use the best tool for the job that you understand how to use. I want to say that going all out functional can be a great way to program, however, it’s not the simplest concept (at least, not if you dive in deep), so please just do what makes sense for you.

That being said, I’d like to show you some great examples of composition to help you learn how to use it and to show you where it can be helpful.

Function Composition

We’ll start with composition of functions, cuz why not? Let’s say you have the following very simple function:

function addAndLog (a, b) {
    let result = a + b;
    console.log(result);
    return result;
}


This seems quite simple, but it can actually be broken down into two completely separate operations: retrieving the result of an operation and logging the result. This means that if you want to get just the result of the operation without logging it, you’re out of luck, so let’s split the operation into a separate function:

function add (a, b) {
    return a + b;
}
function addAndLog (a, b) {
    let result = add(a, b);
    console.log(result);
    return result;
}


Great, now the addition operation can be used anywhere apart from the logging, but that addAndLog is still hard coded to log the result of the add operation rather than being generalized to use the result of any operation. So let’s split the logging functionality out into its own function:

function log (value) {
    console.log(value);
    return value;
}


I added the return statement at the end so that we can add, e.g.:

add(1,2); // returns 3... but we want to log the result too

// so we wrap it:
log(add(1,2)); // logs 3 AND returns 3 so the result can still be used elsewhere


Heck, one of the biggest reasons we can’t just stick with using console.log in this case is because it simply returns undefined. Anyway, these nested invocations of functions are one of the things I like least about functional programming practices because it is essentially read from right to left, which is now how we westerners tend to read.

So, one of the things we can do about it is convert log into a higher order function. A higher-order function is a function that returns a function (simplified definition). The new function, which we’ll call logWrapper will be able to accept a function as an argument, then return a new function that invokes the function you passed in, plus do the logging, plus return the result.

function logWrapper (operation) {
    return function (...args) {
        return log(operation(...args));
    }
}


So now we can create our old addAndLog function like this:

var addAndLog = logWrapper(add);
addAndLog(1,3); // logs 3 and returns 3


Or we can combine it with any other operation, so it’s nice and generic.

That’s composition! You created flexibility by allowing the log function to be composed of any operation plus the logging functionality. Of course, now logWrapper is stuck with the logging functionality. There are several ways to generalize this even further by making a function that can take any number of functions and compose them together for you, but I think you get the idea. There are plenty of other tutorials out there about chaining, or currying, or piping, or composing out there... I just wanted to give you an example.

View/Component Composition

I could just talk about normal object composition, but everybody has done that already. Instead, let’s talk about the composition of views and components (as in React components). Why views and components? Mostly just because everyone is using some sort of framework with views and/or components in them, so it can be more relevant.

React Component Composition

Let’s start with React, despite the fact that I’ve never written about React on this blog. A common example used for mixins is modals or overlays, whatever you want to call them. I think modals can be handled better with composition though:

const Modal = React.createClass({
    render() {
        return (
            <div class="modal">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
                    <h3>this.props.title</h3>
                </div>
                <div class="modal-body">
                    {this.props.children}
                </div>
            </div>
        );
    },
    ... // all the life-cycle stuff
});


Since you are using props.children, you can just nest your view directly inside the Modalcomponent:

ReactDOM.render(<Modal> <MyView/> </Modal>, mountNode);


Or you can use what is called a “higher order component” which is a function that returns a component that wraps your component for you:

function modalify(WrappedComponent) {
    return React.createClass({
        render: function() {
            return (
                <div class="modal">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
                        <h3>this.props.title</h3>
                    </div>
                    <div class="modal-body">
                        <WrappedComponent {...this.props} {...this.state} />
                    </div>
                </div>
            )
        },
        ... // all the life-cycle stuff
  });
}


Now if you want your component to be inside a modal, you can pass your component into a call to modalify and you’ll receive a modal component that will display your component.

ReactDOM.render(modalify(<MyView/>), mountNode);


modalify
uses the JSX spread syntax to pass all props and state down automatically, though it may be more useful to use something like Lodash’s omit function to strip out the modal-specific properties. The interesting thing about this higher order component pattern is that you can pass the wrapped component access to the life-cycle methods, or any other functionality that the modal has access to. For example, if the wrapped component is a form, you may want to close the modal once the form is submitted successfully, so you can pass the closeModal (not actually shown in the example code above) method to WrappedComponent as a property so it can call closeModal once the form is submitted.

You can technically pass access to those methods to MyView in the first nested components example like this:

const Modal = React.createClass({
    render() {
        return (
            <div class="modal">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
                    <h3>this.props.title</h3>
                </div>
                <div class="modal-body">
                    {
                        React.Children.map(this.props.children, child => {
                            return React.cloneElement(child, {
                                closeModal: this.closeModal,
                                ...
                            });
                        })
                    }
                </div>
            </div>
        );
    },
    ... // all the life-cycle stuff
});


Instead of just using {this.props.children}, we use React.Children.map andReact.cloneElement to augment the child views with modal functionality.

If you’d like some more examples of ways React can be composed rather than using inheritance or mixins, check out the post titled “Mixins Considered Harmful” by Dan Abramov. That post is actually what gave me inspiration for this post since it primarily dealt with React and I wanted to go further and demonstrate it with Backbone too, which is what we’ll do now.

Backbone View Composition

You can do pretty much the same thing with Backbone as what we did with React, except Backbone doesn’t have that JSX syntax or as clean a way to pass child views around, but we can still do the same thing with options.

const ModalView = Backbone.view.extend({
    attributes: {
        class: 'modal'
    },
    init: function() {
        _.extend(this.options.childView, {
            closeModal: this.closeModal,
            ...
        });
    },
    render: function() {
        // Ignore the fact that I'm not using a template. Please!
        this.$el.html(
            '<div class="modal-header">' +
                '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>' +
                '<h3>' + this.options.title +</h3>' +
            '</div>' +
            '<div class="modal-body"></div>'
        )
        .find('.modal-body').append(this.options.childView.render());
        return this.$el; // Assume this practice for all `render` methods
    },
    ... // all the life-cycle stuff
});


Then you can use it like this:

let myView = new MyView();
let modal = new ModalView({childView: myView});
$('body').append(modal.render());


You could also use a “higher order view” pattern like we did with React, but I personally believe that nested views make more sense in this case. The higher order view and higher order component patterns are generally more useful if you’re only adding functionality without wrapping the component in more HTML, such as adding a moveTo(x,y) method that animates the positioning of the wrapped component:

function makeAnimatable(WrappedView) {
    return Backbone.View.extend({
        initialize: function(options) {
            this.wrapped = new WrappedView(options);
        },
        moveTo: function(x, y) {
            this.wrapped.$el.animate({
                top: y,
                left: x
            });
        },
        render: function() {
            return this.wrapped.render();
        }
    });
}


This pretty much does it. You’ll probably want to find a way to delegate all method calls to this.wrapped, though. Probably a simple way of doing this though would be to just create a utility function that can be called from anywhere, rather than making moveTo a method:

function moveTo(view, x, y) {
    view.$el.animate({
        top: y,
        left: x
    });
}


But that would be too easy. This is actually one of the advantages to having a language that is both object-oriented (not class-based in the traditional sense, but still object-oriented) and functional. Lone functions can often significantly reduce complexity versus trying to accomplish things with wrappers or inheritance or mixins, etc.

Conclusion

That’s all I’ve got for you today. I hope you’ve learned something useful; Composition, or even just plain functions as shown at the end, can be used to untangle some of the nastiest patterns in object-oriented programming. Just remember: composition over inheritance… and keep it simple, no matter which way you go. God bless and happy coding!

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

Topics:
javascript ,composition

Published at DZone with permission of Joseph Zimmerman, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}