Лука Маттеис
Поведенческое программирование (BP) – парадигма, придуманная в Статья 2012 года Давид Харел, Ассаф Маррон и Гера Вайс.
Непосредственно из абстрактного:
Концепции высокого уровня
Я сначала объясню концепции высокого уровня, используя пример двух компонентов React Components Фильмыный список и FireCount Отказ Одно отображается список фильмов, остальное количество того, сколько фильмов. Тогда я буду погружаться в то, как точно поведенческие программирование.
Оба компонента приводят данные из одного и того же URL HTTP. Они были разработаны двумя разными командами в большой организации. Когда мы визуализируем оба компонента на странице, у нас есть проблема, когда они выполняют тот же запрос:
<>
Мало что мы знали, что это поведенческие компоненты . Это означает, что мы можем сделать что-то совершенно умнее, чтобы избежать оба запроса на стрельбу:
const MoviesCountFromList = withBehavior([ function* () { // block FETCH_COUNT from happening yield { block: ['FETCH_COUNT'] } }, function* () { // wait for FETCH_LIST, requested by the other // MoviesList component, and derive the count const response = yield { wait: ['FETCH_LIST'] } this.setState({ count: response.length }) }])(MoviesCount)В приведенном выше примере мы вошли внутрь FireCount составная часть. Мы ждал и Запрошено за что-то случиться. И, более однозначно для поведенческого программирования, мы также Заблокирован что-то от происхождения.
Потому что мы пытались избежать оба запроса от стрельбы, мы заблокировали Fetch_count событие от срабатывания (так как одинаковые данные уже были приобретены событием fetch_list ).
<>
Добавление функциональности к существующим компонентам без изменения их кода является новизна парадигмы поведенческой программирования.
Интуитивно, это может позволить создать более многоразовые компоненты.
В остальной части статьи я пойду более глубоко в том, как работает поведенческий программирование (BP), в частности, в контексте Реагировать Отказ
Переосмысление процедура программирования
Для достижения вышеуказанных функций нам нужно подумать об поведении программирования немного по-другому. В частности, События Воспроизвести решающую роль в организации синхронизации между различным поведением, которую мы определяем для наших компонентов.
const addHotThreeTimes = behavior( function* () { yield { request: ['ADD_HOT'] } yield { request: ['ADD_HOT'] } yield { request: ['ADD_HOT'] } })const addColdThreeTimes = behavior( function* () { yield { request: ['ADD_COLD'] } yield { request: ['ADD_COLD'] } yield { request: ['ADD_COLD'] } })run( addHotThreeTimes, addColdThreeTimes)
Когда мы запустим вышеуказанный код, мы вернемся в список запрошенных событий:
ADD_HOTADD_HOTADD_HOTADD_COLDADD_COLDADD_COLD
Как и ожидалось, первое поведение выполняется. Как только это сделано, второе поведение продолжается. Тем не менее, новые спецификации для нашего компонента требуют, чтобы мы изменили заказ, в котором обои события срабатывают. Вместо того, чтобы вызвать Add_hot три раза, а потом Add_cold Три раза, мы хотим, чтобы они переплетены и спусковать Add_cold Сразу после Add_hot. . Это будет поддерживать температуру несколько стабильной.
...
const interleave = behavior( function* () { while (true) { // wait for ADD_HOT while blocking ADD_COLD yield { wait: ['ADD_HOT'], block: ['ADD_COLD'] } // wait for ADD_COLD while blocking ADD_HOT yield { wait: ['ADD_COLD'], block: ['ADD_HOT'] } } })run( addHotThreeTimes, addColdThreeTimes, interleave)
В приведенном выше примере мы вводим новое чередование поведения, которое имеет именно то, что нам нужно.
ADD_HOTADD_COLDADD_HOTADD_COLDADD_HOTADD_COLD
Мы изменили Заказать Когда вещи выполняются, не модифицируя код уже письменного поведения.
Процесс обобщен в графике ниже.
Ключевыми понятиями этого способа программирования являются Запрос , Подожди, и Блок операторы. Семантика для этих операторов:
- Запрос Событие: предложение того, что событие рассматривается для запуска, и просить уведомления о том, когда он срабатывает
- В ожидании Событие: не предлагая его запуск, просить быть уведомленным, когда событие запущено
- Блокировка мероприятие: запрещая запуск события, ветовые запросы других B-ниток.
Каждый B-нить (поведенческая нить) живет самостоятельно и не знает о других потоках. Но все они переплетаются во время выполнения, что позволяет им взаимодействовать друг с другом очень новым способом.
Синтаксис генератора необходим для функционирования поведенческой программы. Нам нужно контролировать, когда перейти к следующему утверждению.
Вернуться к реакции
Как эти концепции BP могут использоваться в контексте реагирования?
Оказывается, через компоненты с высоким порядком (HOCS) вы можете добавить эту поведенческую идиому к существующим компонентам в очень интуитивной моде:
class CommentsCount extends React.Component { render() { return {this.state.commentsCount} }}const FetchCommentsCount = withBehavior([ function* () { yield { request: ['FETCH_COMMENTS_COUNT']} const comments = yield fetchComments() yield { request: ['FETCH_COMMENTS_COUNT_SUCCESS']} this.setState({ commentsCount: comments.length }) },])(CommentsCount)Здесь мы используем с Боехавиором , из B-нить Библиотека, чтобы сделать КомментарииCount поведенческий компонент. В частности, мы делаем его извлечь комментарии и отображать данные, как только данные будут готовы.
Для простых компонентов это может не быть таким игровым чейнджером. Но давайте представим более сложные компоненты, с большим количеством логики и других компонентов внутри них.
Мы могли бы представить весь сайт Netflix как
Когда мы используем этот компонент в нашем приложении, мы хотели бы взаимодействовать с ним. В частности, когда нажат фильм, мы не хотим немедленно запускать фильм, но вместо этого мы хотим сделать HTTP-запрос, показать другие данные о фильме, а затем начать фильм.
Без изменяющегося кода внутри
Вместо этого давайте представим, что
const NetflixWithMovieInfo = withBehavior([ function* () { // First, block the MOVIE_START from happening // within until a new // FETCH_MOVIE_INFO_SUCCESS event has been requested. // The yield statement below can be read as: // wait for FETCH_MOVIE_INFO_SUCCESS while blocking MOVIE_START yield { wait: ['FETCH_MOVIE_INFO_SUCCESS'], block: ['MOVIE_START'] } }, function* () { // Here we wait for MOVIE_CLICKED, which is // triggered within , and we fetch our // movie info. Once that's done we request a new event // which the earlier behavior is waiting upon const movie = yield { wait: ['MOVIE_CLICKED'] } const movieInfo = yield fetchMovieInfo(movie) yield { request: ['FETCH_MOVIE_INFO_SUCCESS'], payload: movieInfo } }])(Netflix)Выше мы создали новый NetflixwithMovieinfo Компонент, который изменяет поведение этот фильм_c. Облизывается не будет TR Йгер фильм _Start немедленно.
Вместо этого он использует комбинацию «ожидания при блокировке»: A ждать и а Блок можно определить в течение одного оператора дохода.
Изображение выше описывает, подробнее, что происходит в наших поведенческих компонентах. Каждая небольшая коробка в компонентах является заявление о выходе. Каждая вертикальная пунктирная стрелка представляет собой поведение (aka b-нить).
Внутренне, поведенческая реализация начнет с рассмотрения всех выступлений доходности всех B-ниток на точке текущей синхронизации, изображенной с использованием горизонтальной желтой линии. Он будет продолжаться только к следующему оператору доходности в рамках B-потока, если нет событий в других B-потоках не блокируют его.
Так как ничего не блокирует Movie_clicked. , это будет запрошено. Затем мы можем перейти к следующему утверждению для поведения Netflix. На следующей точке синхронизации B-нить на данный момент, который ждет Movie_clicked , перейдем к своему следующему утверждению.
Среднее поведение, которое ждет и блокировка, не продолжается. Fetch_movie_info_success не было запрошено другими b-нитками, поэтому он все еще ждет и-блоки. Следующая точка синхронизации будет выглядеть что-то подобное:
Как и прежде, мы посмотрим на все заявление о выходе на этой точке синхронизации. На этот раз, однако, мы не можем запросить Movie_start. Потому что есть еще одна B-нить, которая блокирует его (отчет о черном доходе). Поэтому компонент Netflix не запустит фильм.
Fetch_movie_info_success. Однако на данный момент свободно быть запрошенным. Это будет разблокировать Movie_start. на следующей точке синхронизации.
Все это на практике позволило нам изменить порядок вещей, происходящих в других компонентах, без прямого изменения их кода. Мы смогли заблокировать определенные события от стрельбы до того, как другие условия не будут выполнены в других компонентах.
Это меняет способ, которым мы могли бы подумать о программировании: Не обязательно набор утверждений, выполненных в порядке, но скорее чередование выходов выступлений все синхронизировано через конкретную семантику событий.
Вот простая анимация, изображающая способ B-потоков выполнены и переплетены во время выполнения.
Программирование без изменения старого кода
Есть еще один способ понять это программирование идиома. Мы можем сравнить путь, который мы в настоящее время проводим в качестве изменений спецификаций, по сравнению с тем, как это будет сделано с поведенческим программированием.
В приведенной выше подписи мы представляем, как поведение может быть добавлено в ненациональную программу. Начнем с программы, описанной только с помощью трех черных прямоугольников (слева).
Как изменение спецификаций, мы понимаем, что нам нужно изменить программу и добавить новое поведение в различных разделах программы, изображенной как вновь добавленные цветные прямоугольники. Мы продолжаем делать это как требования для нашего изменения программного обеспечения.
Каждое добавление поведения требует от нас, чтобы изменить код, который был написан, который, возможно, помещает старое поведение с ошибками. Кроме того, если программа, которую мы меняем, является частью различных других модулей, используемых разными людьми, мы могли бы внести нежелательное поведение в их программное обеспечение. Наконец, может быть невозможно изменить определенные программы, поскольку они могут быть распространены как библиотеки с лицензированным исходным кодом.
В приведенном выше показателе мы видим, как могут быть достигнуты те же модификации программы, используя идиомы поведенческого программирования. Мы все еще начинаем с наших трех прямоугольников слева, когда мы сделали раньше. Но по мере возникновения новых спецификаций мы их не модифицируем. Вместо этого мы добавляем новые B-потоки, представленные как столбцы.
Полученную программу одинаково, хотя и построен по-разному. Одним из преимуществ поведенческого подхода является то, что нам не нужно изменять старый код в качестве изменения требований.
Вы также можете представить развитие каждой B-нитки параллельно, возможно, разными людьми в большой организации, поскольку они не напрямую зависят друг от друга.
Преимущество этого подхода также похоже на упаковку: мы можем изменить поведение библиотеки без необходимости доступа или изменять свой исходный код.
API не только как реквизит, но как события
В настоящее время единственным способом для комбинации реагирования на связи с внешним миром является через реквизиты (кроме API контекста).
Составляя компонент поведенческий, а не использовать реквизиты, мы говорим о внешнем мире, когда все происходит в компоненте, получая события.
Разрешить другим разработчикам взаимодействовать с поведением компонента, поэтому мы должны документировать события, которые он Запросы события это ждет, и, наконец, события это Блоки Отказ
События становятся новыми API.
Например, в не поведенческом Счетчик Компонент, мы говорим внешний мир, когда счетчик увеличивается, и какое количество текущего подсчета, используя OninCrement проп
class Counter extends React.Component { state = { currentCount: 0 } handleClick = () => { this.setState(prevState => ({ currentCount: prevState.currentCount + 1 }), () => { this.props.onIncrement(this.state.currentCount) }) } render() { {this.state.currentCount} }}console.log(currentCount) }/>
Что если мы хотим сделать что-то еще, прежде чем государство счетчика увеличивается? Действительно, мы могли бы добавить новую опору, например onbeforeIncrement С Но точка в том, что мы не хотим добавлять реквизит и код рефакторов каждый раз, когда возникает новый конкретный.
Если мы преобразуем его в поведенческий компонент, мы можем избежать рефакторинга, когда появляются новые спецификации:
class Counter extends React.Component { state = { currentCount: 0 } handleClick = () => { bp.event('CLICKED_INCREMENT') } render() { {this.state.currentCount} }}const BehavioralCounter = withBehavior([ function* () { yield { wait: ['CLICKED_INCREMENT'] } yield { request: ['UPDATE_CURRENT_COUNT'] } this.setState(prevState => ({ currentCount: prevState.currentCount + 1 }), () => { this.props.onIncrement(this.state.currentCount) }) }])(Counter)Обратите внимание, как мы переместили логику, когда состояние обновляется внутри B-нить. Кроме того, до того, как на самом деле происходит обновление, новое событие Update_current_count запрашивается.
Это эффективно позволяет другим B-потокам блокировать обновление от происхождения.
Компоненты также могут быть инкапсулированы и совместно используются в виде разных пакетов, и пользователи могут добавлять поведение, поскольку они видят Fit.
// package-name: movies-listexport const function MoviesList() { ...}// package-name: movies-list-with-paginationexport const MoviesListWithPagination = pipe( withBehavior(addPagination))(MoviesList)
// package-name: movies-list-with-pagination-logicexport const MoviesListWithDifferentPaginationLogic = pipe( withBehavior(changePaginationLogic))(MoviesListWithPagination)
Опять же, это отличается от простого улучшения компонента, как будет делать обычный HOC. Мы можем заблокировать определенные вещи, произошедшие в компонентах, которые мы подвергаем, эффективно изменяя их поведение.
Заключение
Эта новая программирование IDIOM может почувствовать себя некомфортно, но, похоже, облегчает видную проблему, имею в виду, когда используете компоненты интерфейса: Трудно повторно использовать компоненты, потому что они не смешаются с окружающей средой, в которую они были введены.
В будущем, возможно, используя эти поведенческие концепции, мы сможем добавить новое поведение в приложения, просто устанавливая новые компоненты. Такие вещи, как это будет возможно:
Кроме того, события не нужно загрязнять все приложение и могут транслироваться только в пределах определенной среды.
Спасибо за прочтение! Если вы заинтересованы в реальной реализации поведенческого программирования, см. Мой текущая работа в библиотеке прогресса, которая работает с React: https://github.com/lmatteis/b-thread. . Домашняя страница поведенческой программирования Также содержит различные реализации.
Для получения дополнительной информации об этой захватывающей новой концепции я предлагаю вам прочитать Научные документы по поведенческому программированию или проверьте некоторые из мои другие статьи на тему Отказ
Оригинал: “https://www.freecodecamp.org/news/an-intro-to-behavioral-programming-with-react-request-wait-and-block-ad876e2d235e/”