Коллекция разнотипных объектов в Backbone.js

В коллекциях Backbone.js не обязательно должны храниться объекты одного типа. Если в неё явно добавить экземпляр класса, унаследованного от Backbone.Model, то он без изменений будет помещён в неё. У коллекции есть атрибут model, в котором хранится конструктор модели. Когда в коллекцию добавляется простой объект, то он в качестве параметра передаётся в этот конструктор.

В атрибуте model можно, фактически, указать любую функцию. Возвращаемые значения (экземпляры классов, унаследованных от Backbone.Model) будут помещаться в коллекцию. Такие функции ещё называют фабриками или фабричными методами. В документации к атрибуту model как раз представлен вариант такой фабрики.

Чтобы каждый раз не писать однотипных проверок в подобных ситуациях, я предлагаю пользоваться паттерном «Фабрика фабрик»:

App.Factory = function (getter, hash, def) {
  return function () {
    var value = getter.apply(this, arguments);
    var ctor = hash[value] ? hash[value] : def;
    return new ctor(arguments[0], arguments[1]);
  };
};
  • getter — функция, которая возвращает значение, на основании которого будет выбираться требуемый конструктор;
  • hash — объект ключ-значение с перечислением конструкторов, которые могут использоваться фабрикой;
  • def — конструктор по-умолчанию.

Предположим, что в коллекции могут находиться объекты следующих классов:

App.Item = Backbone.Model.extend({});
App.ItemNews = App.Item.extend({});
App.ItemAction = App.Item.extend({});
App.ItemMessage = App.Item.extend({});

Таким образом, фабрика объектов будет выглядеть так:

App.ItemFactory = App.Factory(
  function (attr) { return attr.type; },
  {
    "news": App.ItemNews,
    "action": App.ItemAction,
    "message": App.ItemMessage
  },
  App.Item
);

App.Factory возвращает функцию, которая, в свою очередь, будет возвращать требуемый экземпляр в зависимости от значения атрибута type. Эту функцию и передаём в описание коллекции.

App.Items = Backbone.Collection.extend({
  model: App.ItemFactory
});

Теперь коллекция будет заполняться моделями нужного типа автоматически.

Практическое применение паттерна можно посмотреть в исходном коде примера.