Дизайн кода: организация JavaScript
Великолепный дизайн — это результат заботливого и внимательного отношения к тому, что важно, который в итоге приводит к полезному, понятному и, надо надеяться, красивому интерфейсу. Не обманывайте себя, дизайн это удел не только дизайнеров.
Существует множество дизайнерских решений в программировании, и говоря это, я подразумеваю дизайн не пользовательского интерфейса, а дизайн самого кода.
Хорошо спроектированный код намного легче поддерживать, улучшать и расширять, что позволяет разработчикам быть эффективнее. Это приводит к тому, что больше внимания и энергии будет потрачено на значимые вещи, что в свою очередь осчастливит всех — пользователей, разработчиков, все заинтересованные стороны.
Существуют три действительно важных высокоуровневых, языконезависимых аспекта дизайна кода:
- Архитектура — программная база для всего кода. Правила, определяющие, как различные компоненты (модель, представление и контроллер) взаимодействуют друг с другом.
- Поддерживаемость – насколько хорошо код может быть улучшен или расширена его функциональность?
- Повторное использование — насколько возможно повторное использование отдельных компонентов приложения? Насколько гибка в настройке каждая отдельная реализация каждого компонента?
В менее удачных языках, например в 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.
О дизайне кода важно думать даже если вы разрабатываете что-то, что не будет реально использоваться. В будущем вы будете благодарны себе за это.