понедельник, 10 февраля 2014 г.

[Из песочницы] Использование абстракции — наблюдателя в javaScript программировании

В функциональном программировании мы редко заботимся о примитивах параллелизма, таких как потоки. Вместо этого мы используем различные абстракции, делающие наш код более легким для понимания. В JavaScript наблюдатель – абстракция, которую мы встречаем чаще всего. Она обычно ассоциируется с AJAX – запросами. Мы часто используем абстракции для рассуждения о нашем параллельном коде, но эти абстракции могут применяться и в других ситуациях. Высокоуровневые пользовательские интерфейсы, по существу, это большое количество асинхронных событий, которые могут быть представлены, как таковые, в коде.

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


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




Создание наблюдателей при пользовательском взаимодействии.

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


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



class UsSes
construct: (@Q, @InputModals, @Session) ->
@_session = @Session.get()

signIn: ->
@_authDeferred = @Q.defer()
@_bayOrPrompt()
@_authDeferred.promise

utterSignIn: (data) ->
@Session.save(data).then(@_utterAuth)

_resolveOrPrompt: ->
if @_session?.signedIn
@_utterAuth(@_session)
else
@AuthModals.openSignIn()
_utterAuth: (session) ->
@_session = session
@_authDeferred.resolve(session.user)




Теперь немного о том, что здесь происходит. C – это библиотека Криса Коваля, предназначенная для работы с наблюдателями. Мы не будем говорить о реализации InputModals и Session. Мы только предположим, что InputModals вызывает модальное окно и что либо может вызвать utterSignIn, когда форма авторизации отправляется. Session делает 2 вещи. Она спрашивает сервер, если мы уже прошли авторизацию, и отправляет AJAX запрос на сервер, возвращая наблюдатель с объектом сессии.

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

Хорошие паттерны создаются, что бы быть реализованными единажды.

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

class UserBays
construct: (@Q, @UsSes, @BayModal) ->

bay: (@video) =>
@UsSes.input ().then(@_promiseBay (video))

totalBay: (license)
@user.addLicense(@video, license)
@_purchaseDeferred.resolve(license)

_promisePurchase: (video) => (@user) =>
@_bayDeferred = @Q.defer()
@_resolveOrPrompt(video)
@_bayDeferred.promise

_resolveOrPrompt: (video) =>
if @user.his (video)
@utterBay (@user.licenseFor(video))
else<br />
@ BayModal.openBayForm(video)




Мы применили тот же паттерн здесь. Если у пользователя уже есть собственное видео, мы создаем наблюдателя с правами немедленно. Если нет, мы предлагаем пользователю купить его и создать наблюдатель когда лицензия будет получена позже. Пользователь может купить только 1 видео за раз. Если пользователь закрывает модальное окно и пытается купить еще что-нибудь, старые права могут быть удалены.

Наконец, мы можем скрыть все это внутри объекта, который отвечает за проигрывание видео:

class VideoPlayer
constructor: (@UserPurchases, @PlayerModal, @Video) ->

play: (video) =>
@UserPurchases.purchase(video).then(@_loadAndPlay(video))

_loadAndPlay: (video) => (license) =>
@Video.load(video, license).then(@PlayerModal.openPlayer)





Выше код, в котором для проигрывания видео нужно только вызвать функцию VideoPlayer.play. Ему не нужно заботиться о себе, независимо от того, прошел ли пользователь авторизацию, или от того, если у пользователя свой контент. Нам бы до сих пор нужно было бы иметь флаг внутри шаблона, что бы решить, показывать кнопку “Купить” или “Проигрывать”. Однако, если мы используем Null Object pattern и передаем шаблону Guest, когда пользователь не авторизован, это все еще остается сравнительно просто.

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


Такие ситуации встречаются повсюду, если вы присмотритесь. Необходимо помнить, что асинхронность применима не только для параллелизма, и создается не только для XHR.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий