Polymorphic models

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


Основы полиморфизма

В Backbone.js базовой единицей данных является Model. Полиморфная модель строится на наследовании и композиции: создается базовая модель с общими атрибутами, а затем от неё наследуются конкретные типы моделей с уникальными полями и методами.

Пример базовой модели:

var BaseItem = Backbone.Model.extend({
  defaults: {
    id: null,
    type: 'base',
    name: ''
  },

  initialize: function() {
    console.log('Создана базовая модель:', this.get('type'));
  },

  describe: function() {
    return this.get('name') + ' (' + this.get('type') + ')';
  }
});

Базовая модель определяет общие поля (id, type, name) и метод describe, который будет переопределяться или расширяться в дочерних моделях.


Наследование и создание специализированных моделей

Полиморфизм реализуется через наследование:

var Book = BaseItem.extend({
  defaults: _.extend({}, BaseItem.prototype.defaults, {
    type: 'book',
    author: ''
  }),

  describe: function() {
    return this.get('name') + ' by ' + this.get('author');
  }
});

var Movie = BaseItem.extend({
  defaults: _.extend({}, BaseItem.prototype.defaults, {
    type: 'movie',
    director: ''
  }),

  describe: function() {
    return this.get('name') + ', directed by ' + this.get('director');
  }
});

Ключевые моменты:

  • Использование _.extend для расширения базовых defaults.
  • Переопределение методов для обеспечения специфического поведения.

Коллекции полиморфных моделей

Коллекция может содержать разные типы моделей, но предоставлять единый интерфейс для работы с ними.

var Library = Backbone.Collection.extend({
  model: function(attrs, options) {
    switch(attrs.type) {
      case 'book':
        return new Book(attrs, options);
      case 'movie':
        return new Movie(attrs, options);
      default:
        return new BaseItem(attrs, options);
    }
  }
});

Особенности:

  • Коллекция автоматически создаёт экземпляры нужного типа.
  • Это упрощает работу с данными из сервера, где разные элементы могут иметь разные структуры.

Пример использования:

var items = new Library([
  {id: 1, type: 'book', name: '1984', author: 'George Orwell'},
  {id: 2, type: 'movie', name: 'Inception', director: 'Christopher Nolan'}
]);

items.each(function(item) {
  console.log(item.describe());
});

Результат будет корректно выводить описание каждого элемента в зависимости от его типа.


Работа с сервером

Backbone.js позволяет синхронизировать модели с сервером через fetch, save и destroy. Для полиморфных моделей важно правильно обрабатывать сериализацию и десериализацию.

var BaseItem = Backbone.Model.extend({
  defaults: { id: null, type: 'base', name: '' },

  parse: function(response) {
    switch(response.type) {
      case 'book':
        return new Book(response);
      case 'movie':
        return new Movie(response);
      default:
        return new BaseItem(response);
    }
  }
});

Использование метода parse позволяет Backbone автоматически создавать правильный тип модели при получении данных с сервера.


Расширение полиморфизма через миксины

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

var Sharable = {
  share: function(platform) {
    console.log('Sharing', this.get('name'), 'on', platform);
  }
};

_.extend(Book.prototype, Sharable);

var book = new Book({name: '1984', author: 'George Orwell'});
book.share('Twitter'); // Sharing 1984 on Twitter

Миксины позволяют гибко добавлять функциональность к любому типу модели, не создавая глубокую цепочку наследования.


Полиморфные представления

Для работы с полиморфными моделями часто создаются динамические представления, которые выбирают шаблон в зависимости от типа модели:

var ItemView = Backbone.View.extend({
  tagName: 'div',

  render: function() {
    var type = this.model.get('type');
    if (type === 'book') {
      this.$el.html('<h3>Book: ' + this.model.get('name') + '</h3>');
    } else if (type === 'movie') {
      this.$el.html('<h3>Movie: ' + this.model.get('name') + '</h3>');
    } else {
      this.$el.html('<h3>Item: ' + this.model.get('name') + '</h3>');
    }
    return this;
  }
});

Такой подход обеспечивает единый механизм рендеринга коллекций с различными типами элементов.


Преимущества полиморфных моделей

  • Гибкость: возможность объединять разные типы данных в одной коллекции.
  • Повторное использование кода: общие методы и свойства определяются в базовой модели.
  • Расширяемость: легко добавлять новые типы моделей без изменения существующего кода.
  • Совместимость с сервером: через методы parse и кастомные фабрики моделей можно обрабатывать любые типы данных.

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