Дизайн кода: организация JavaScript

Великолепный дизайн — это результат заботливого и внимательного отношения к тому, что важно, который в итоге приводит к полезному, понятному и, надо надеяться, красивому интерфейсу. Не обманывайте себя, дизайн это удел не только дизайнеров.

Существует множество дизайнерских решений в программировании, и говоря это, я подразумеваю дизайн не пользовательского интерфейса, а дизайн самого кода.

Хорошо спроектированный код намного легче поддерживать, улучшать и расширять, что позволяет разработчикам быть эффективнее. Это приводит к тому, что больше внимания и энергии будет потрачено на значимые вещи, что в свою очередь осчастливит всех — пользователей, разработчиков, все заинтересованные стороны.

Существуют три действительно важных высокоуровневых, языконезависимых аспекта дизайна кода:

  1. Архитектура — программная база для всего кода. Правила, определяющие, как различные компоненты (модель, представление и контроллер) взаимодействуют друг с другом.
  2. Поддерживаемость – насколько хорошо код может быть улучшен или расширена его функциональность?
  3. Повторное использование — насколько возможно повторное использование отдельных компонентов приложения? Насколько гибка в настройке каждая отдельная реализация каждого компонента?

В менее удачных языках, например в JavaScript, для написания удобного и приятного кода потребуется немного самодисциплины. JavaScript-окружение настолько дружелюбно к разработчику, что можно «разбросать» куски кода по разным углам вашего приложения — устроить настоящий бардак, но тем временем приложение всё равно будет работать. Грамотное планирование и разработка архитектуры на раннем этапе развития (и следование ей в дальнейшем) привнесёт в ваш код ограничения, которые позволят вам добиться согласованности.

Я хочу показать вам один проверенный временем паттерн разработки, которым горжусь — паттерн «Модуль», чья расширяемая структура дополнит вашу архитектуру до состояния стройной, поддерживаемой и расширяемой программной базы. Мне нравится создавать модули с помощью системы jQuery-плагинов, так как это обеспечивает удивительную возможность повторного использования, набор удобных опций и превосходно созданное API.

Ниже по тексту я расскажу вам, как писать ваш код организуя его в модули, которые можно будет использовать в ваших следующих проектах.

Паттерн «Модуль»

Существует множество паттернов разработки и не меньше ресурсов, где с ними можно ознакомиться. Эдди Османи (Addy Osmani) написал потрясающую (бесплатную!) книгу о паттернах в JavaScript, которую я настоятельно рекомендую разработчикам любого уровня.

Паттерн Модуль позволяет структурировать ваш код и поддерживать его в хорошо организованном состоянии. «Модуль» это обычный объект, который содержит методы и свойства. Лучшее в нём — его простота: даже тот, кто не знаком с классическими паттернами разработки, посмотрев на него, сможет понять, как он работает.

В приложениях, которые используют этот паттерн, каждый компонент заключен в собственный модуль. Например, чтобы добавить функцию автодополнения текста, вы создаёте модуль, который работает с текстовым полем, и модуль для списка вариантов дополнения. Эти два модуля будут работать вместе, но модуль, который работает с текстовым полем, не работает со списком вариантов и наоборот.

Именно из-за такого разделения задач компонентов паттерн «модуль» отлично подходит для создания надёжной архитектуры системы. Отношения внутри приложения чётко определены. Всё, что касается текстового поля, выполняется с помощью соответствующего модуля, функционал не рассредоточен по всему коду и, как результат, остаётся чистым.

Ещё одним достоинством модульной организации является простота её поддержки. Модули можно улучшать и оптимизировать независимо, не затрагивая никаких других частей приложения.

Я использовал паттерн «Модуль» для создания структуры jPanelMenu, плагина jQuery, который создаёт меню, скрывающееся за пределами экрана. Именно его я и использую для того, чтобы проиллюстрировать процесс создания модуля.

Создание модуля

Сначала я определяю три метода и свойство, которые определяют взаимодействие с меню.

var jpm = {
    animated: true,
    openMenu: function( ) {
        …
        this.setMenuStyle( );
    },
    closeMenu: function( ) {
        …
        this.setMenuStyle( );
    },
    setMenuStyle: function( ) { … }
};

Идея заключается в том, чтобы разбить код на минимальные блоки, пригодные для повторного использования. Я мог бы просто написать метод toggleMenu(), но создание методов openMenu() и closeMenu() обеспечивает лучший контроль и большие возможности повторного использования внутри модуля.

Обратите внимание, что обращения к методам и свойствам внутри модуля (например, вызов setMenuStyle()) происходят с использованием this.

Это основная структура модуля. Можно продолжать добавлять методы и свойства, но это будет не сложнее, чем добавление тех, что уже есть. После того, как структура готова, на её основе можно добавить то, что нужно для повторного использования модуля: настройки и API.

jQuery-плагины

Третий аспект проектирования кода, возможно, самый важный: пригодность его к повторному использованию. И тут есть подвох. Да, можно написать на чистом JavaScript и внедрить модули, пригодные для повторного использования (мы на 90% закончили модуль, который описан выше), но я по ряду причин предпочитаю для более сложных вещей использовать jQuery-плагины.

Самая важная из этих причин заключается в ненавязчивых связях. Если для построения компонента используется jQuery, то очевидно, что для его имплементации он нужен.

Кроме того, код, с помощью которого вы имплементируете компонент, будет однороден с остальным кодом проекта, использующего jQuery. Это хорошо с точки зрения эстетики и, кроме того, делает код предсказуемым: разработчик сможет понять как работать с плагином без длительного изучения документации. Это ещё один способ создать удобный для разработчиков интерфейс.

Прежде чем начать писать jQuery-плагин, нужно удостовериться, что он не конфликтует с другими JavaScript библиотеками, которые используют $-нотацию. Это проще, чем может показаться:

(function($) {
    // код jQuery-плагина
})(jQuery);

Плагин это просто метод, определённый в объекте jQuery. Помещаем уже созданный нами модуль внутрь него.

(function($) {
    $.jPanelMenu = function( ) {
        var jpm = {
            animated: true,
            openMenu: function( ) {
                …
                this.setMenuStyle( );
            },
            closeMenu: function( ) {
                …
                this.setMenuStyle( );
            },
            setMenuStyle: function( ) { … }
        };
    };
})(jQuery);

Всё, что нужно для вызова плагина — вызвать созданную вами функцию.

var jpm = $.jPanelMenu( );

Настройки

Настройки необходимы для любого приспособленного для повторного использования плагина, ведь они позволяют приводить его к требованиям каждого конкретного проекта, который несёт с собой массу специфических стилей, способов взаимодействия и структур данных.

Считается хорошим тоном по умолчанию устанавливать в настройках хорошо продуманные значения. Самый простой способ это сделать — использовать метод jQuery $.extend( ), который принимает 2 и более аргументов.

В качестве первого аргумента $.extend( ) передаётся объект, содержащий все настраиваемые свойства и их значения по умолчанию. В качестве второго аргумента передаётся объект, который содержит настройки, переданные пользователем. Метод $.extend() объединит эти два объекта, перезаписывая стандартные настройки теми, которые передал пользователь.

(function($) {
    $.jPanelMenu = function(options) {
        var jpm = {
            options: $.extend({
                'animated': true,
                'duration': 500,
                'direction': 'left'
            }, options),
            openMenu: function( ) {
                …
                this.setMenuStyle( );
            },
            closeMenu: function( ) {
                …
                this.setMenuStyle( );
            },
            setMenuStyle: function( ) { … }
        };
    };
})(jQuery);

В то время как вы предоставляете полезные стандартные настройки для вашего плагина, сами настройки своими названиями могут объяснять своё предназначение, что позволяет понять, что делает код с первого взгляда.

Старайтесь предоставлять все настройки, которые могут понадобиться разработчику. Гибкость поможет внедрять плагин в последующие проекты, и, к тому же, гибкие настройки никогда не причиняли вреда.

API

Настройки — отличный способ контролировать поведение плагина. API, с другой стороны, позволяет расширять его функциональные возможности, предоставляя доступ к свойствам и методам в процессе имплементации.

Хорошо, когда API дает максимум возможностей, однако не стоит давать доступ ко всем внутренним методам и свойствам. В идеале вам стоит предоставить доступ только к тем из них, которые будут реально использоваться.

В нашем примере API должен содержать методы, которые позволят открыть и закрыть меню, ничего более. Внутренний метод setMenuStyle() вызывается, когда меню открывается или закрывается, но публичный доступ к нему не нужен.

Чтобы дать доступ к API, нужно сделать так, чтобы в конце кода плагина возвращался объект с его методами и свойствами. Можно даже связать эти методы и свойства с теми, которые используются внутри модуля, именно в этом случае достоинства паттерна «Модуль» видны лучше всего.

(function($) {
    $.jPanelMenu = function(options) {
        var jpm = {
            options: $.extend({
                'animated': true,
                'duration': 500,
                'direction': 'left'
            }, options),
            openMenu: function( ) {
                …
                this.setMenuStyle( );
            },
            closeMenu: function( ) {
                …
                this.setMenuStyle( );
            },
            setMenuStyle: function( ) { … }
        };
        return {
            open: jpm.openMenu,
            close: jpm.closeMenu,
            someComplexMethod: function( ) { … }
        };
    };
})(jQuery);

Методы и свойства API будут доступны из объекта, который возвращается при инициализации плагина.

var jpm = $.jPanelMenu({
    duration: 1000,
    …
});
jpm.open( );

Улучшение интерфейсов

Чтобы облегчить себе жизнь, мы, руководствуясь простыми правилами, создали расширяемый, подходящий для повторного использования плагин. Поэкспериментируйте, чтобы разобраться, подходит ли он вам и вашей команде, вписывается ли в рамки вашего рабочего процесса.

Каждый раз, когда пишу код, который вероятно можно будет использовать повторно, я создаю модульный jQuery-плагин. Лучшее в этом подходе то, что он заставляет использовать и тестировать написанный вами код, и в процессе вы быстро выясняете его сильные стороны и недостатки, а значит, можете планировать изменения.

В результате на выходе у нас оттестированный, готовый к открытию для сторонних разработчиков или продаже код. Свои лучшие плагины я выложил на GitHub.

О дизайне кода важно думать даже если вы разрабатываете что-то, что не будет реально использоваться. В будущем вы будете благодарны себе за это.