Статьи

Что Flex научил меня привязке данных

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

Чтобы понять, почему привязка данных настолько революционна, давайте рассмотрим стандартную настройку Model-View-Controller для создания пользовательских интерфейсов.

                         

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

модель

package example.flextesting {
[Bindable]
public class LoginModel {
username:String;
password:String;
showError:String;
}
}

Теперь модель сама по себе не так интересна, так как нам нужно добавить поведение. Чтобы сохранить разделение проблем, мы помещаем все поведение в контроллер.

контроллер

package example.flextesting {
public class LoginController {
public var model:LoginModel;
public var authenticator:Function;
public function login():void {
showError = authenticator(username, password);
}
}
}

Проблема с котельной

Без привязки данных мы должны написать много кода для котельной пластины, который копирует данные из модели в представление. В нашем примере это может быть просто, поскольку у нас есть только три поля для копирования, но в сложных формах это может быть несколько сотен объектов для копирования. Со временем это становится настолько сложным, что добавление нового поля требует, чтобы мы изменили многие вещи в унисон: представление, модель и код, который копирует его в представление.

Проблема круговой зависимости

На данный момент нам нужно представление, чтобы иметь возможность выложить его в MXML. Теперь представьте, что нет привязки данных. У нас есть проблема, нам нужно уведомить представление для обновления самого себя из модели, и нам нужно получить представление для вызова функции login () в контроллере. Это создает круговую зависимость между контроллером и представлением, и просто нет возможности обойти это. Это означает, что мы не можем использовать конструктор зависимости-инъекции.

Тестовый Кошмар

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

Один из способов решить эту циклическую зависимость — создать интерфейс для представления. Таким образом, мы можем иметь две реализации представления. Один — это реальное представление, а второй — фиктивная реализация этого представления. В тестах мы можем просто создать экземпляр макета, и мы дома свободны. Хотя это хорошее решение, оно требует от нас написания дополнительных интерфейсов и дополнительных реализаций, и поэтому оно громоздко. Хуже того, любые изменения в представлении требуют, чтобы мы изменили 4 файла (контроллер, интерфейс, представление и макет). Можем ли мы сделать лучше?

Привязка данных меняет зависимости

На помощь приходит привязка данных. Привязка данных решает нашу круговую проблему очень умным способом. Это делает так, чтобы контроллер не имел ссылки на представление. View по-прежнему распознает контроллер, так как первый должен вызывать методы последнего, когда пользователь взаимодействует с ним, но циклическая зависимость нарушена. Еще лучше то, что оставшаяся зависимость — от представления к контроллеру, а не наоборот. Это очень важно, потому что это позволяет нам тестировать контроллер изолированно: мы можем легко создать экземпляр модели и контроллера без использования зависимости вида.

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:flextesting="example.flextesting.*">
<flextesting:LoginController id="cntl"/>
<mx:Form>
<mx:FormItem label="Username:">
<mx:TextInput text="{controller.model.username}"
change="cntl.model.username =
event.currentTarget.text"/>
</mx:FormItem>
<mx:FormItem label="Password:">
<mx:TextInput text="{controller.model.password}"
change="cntl.model.password =
event.currentTarget.text"
displayAsPassword="true"/>
</mx:FormItem>
<mx:FormItem label="Label">
<mx:HBox>
<mx:Button label="Login"
click="controller.login()"/>
<mx:Label text="Login Failed"
visible="{cntl.model.showError}"
color="#FF0000" fontWeight="bold"/>
</mx:HBox>
</mx:FormItem>
</mx:Form>
</mx:VBox>

Магия привязки данных исходит из фигурных скобок ‘{}’. Как вы можете видеть в MXML выше, TextInput и Label управляются символом {}. Привязка данных действует как своего рода обратная зависимость, свойство, которое очень приветствуется, так как мы пытаемся изолировать контроллер.

То, что я хочу, это обратная привязка данных

К сожалению, Flex недостаточно продвинулся в привязке данных. Когда модель меняется, вид также меняется. Но так как представление является TextField, мы также хотим, чтобы модель изменялась, когда пользователь изменяет форму. Это обратное связывание данных недоступно во Flex из коробки, поэтому мы должны смоделировать его, добавив события изменения, которые копируют данные обратно в модель. Вы можете увидеть это в вышеприведенном MXML, прямое связывание данных {controller.model.username} и наоборот, создают много дополнительного кода, который загромождает приложение.

Вывод

Итак, что мы узнали о привязке данных:

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