En En

Внедрение зависимости


Приложения Ember используют шаблон разработки "внедрение зависимости" (DI), чтобы объявлять классы объектов, создавать объекты этих классов, а также вводить зависимости между ними. Приложения и их экземпляры исполняют свою роль в реализации DI.

Ember.Application служит в качестве «реестра» для объявлений зависимостей. Приложение регистрирует фабрики (то есть классы), а также правила «внедрения» зависимостей, которые применяются при создании объектов класса.

Ember.ApplicationInstance выступает в качестве «владельца» объектов, созданных из зарегистрированных фабрик. Экземпляры приложения предоставляют средства для «поиска» (то есть создания и/или возвращения) объектов.

Примечание: хотя Application служит исходным реестром для приложения, каждый ApplicationInstance также может выполнять эту роль. Регистрация на уровне экземпляра пригодится для предоставления индивидуальных настроек на уровне экземпляра, например для A/B тестирования функции.

Регистрация фабрик

Фабрика может представлять любую часть приложения, например маршрут, шаблон или индивидуально-настроенный класс. Каждая фабрика регистрируется с определенным ключом. Например, шаблон index регистрируется с ключом template:index, а маршрут application — с ключом route:application.

Регистрационные ключи содержат два сегмента, разделенных двоеточием (:). Первый сегмент — тип фабрики фреймворка, второй — имя конкретной фабрики. Поэтому шаблон index имеет ключ template:index. В Ember есть несколько встроенных типов фабрик, например service (служба), route (маршрут), template (шаблон) и component (компонент).

Вы можете создать собственный тип фабрики, если зарегистрируете фабрику с новым типом. Например, чтобы создать тип user, вам нужно зарегистрировать фабрику так: application.register('user:user-to-register').

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

Например, через инициализатор приложения можно зарегистрировать фабрику Logger с ключом logger:main:

app/initializers/logger.js

import Ember from 'ember';

export function initialize(application) {
  var Logger = Ember.Object.extend({
    log(m) {
      console.log(m);
    }
  });

  application.register('logger:main', Logger);
}

export default {
  name: 'logger',
  initialize: initialize
};

Регистрация уже созданных объектов класса

По умолчанию Ember пытается создать объект зарегистрированной фабрики, когда ее находит. При регистрации уже созданного объекта вместо класса, используйте опцию instantiate: false, чтобы избежать повторного создания объекта класса во время поиска.

В следующем примере logger — простой объект JavaScript, который при нахождении вернется «как есть»:

app/initializers/logger.js

export function initialize(application) {
  var logger = {
    log(m) {
      console.log(m);
    }
  };

  application.register('logger:main', logger, { instantiate: false });
}

export default {
  name: 'logger',
  initialize: initialize
};

Регистрация синглтонов и несинглтонов

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

Если вы хотите создавать новые объекты при каждом поиске, регистрируйте ваши фабрики как несинглтоны с помощью опции singleton: false.

В следующем примере класс Message зарегистрирован как несинглтон:

app/initializers/notification.js

import Ember from 'ember';

export function initialize(application) {
  var Message = Ember.Object.extend({
    text: ''
  });

  application.register('notification:message', Message, { singleton: false });
}

export default {
  name: 'notification',
  initialize: initialize
};

Внедрение фабрик

Если фабрика зарегистрирована, ее можно "внедрить" туда, где это необходимо.

Фабрики можно внедрять по типу. Например:

app/initializers/logger.js

import Ember from 'ember';

export function initialize(application) {
  var Logger = Ember.Object.extend({
    log(m) {
      console.log(m);
    }
  });

  application.register('logger:main', Logger);
  application.inject('route', 'logger', 'logger:main');
}

export default {
  name: 'logger',
  initialize: initialize
};

В результате из всех фабрик типа route будут созданы объекты с внедренным свойством logger. Значение logger будет происходить из фабрики под именем logger:main.

Маршруты в этом примере теперь могут получить доступ к внедренному logger:

app/routes/index.js

import Ember from 'ember';

export default Ember.Route.extend({
  activate() {
    // The logger property is injected into all routes
    this.get('logger').log('Entered the index route!');
  }
});

Можно также внедрить конкретную фабрику с помощью ее полного ключа:

application.inject('route:index', 'logger', 'logger:main');

В этом случае logger будет внедрен только на маршруте index.

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

Специальные внедрения

Внедрение зависимостей также можно объявить напрямую в классах Ember с помощью Ember.inject. Сейчас Ember.inject поддерживает внедрение контроллеров (через Ember.inject.controller) и служб (через Ember.inject.service).

Следующий код внедряет службу shopping-cart в компонент cart-contents в качестве свойства cart:

app/components/cart-contents.js

import Ember from 'ember';

export default Ember.Component.extend({
  cart: Ember.inject.service('shopping-cart')
});

Если вы хотите внедрить службу с тем же именем, что и у свойства, просто оставьте имя службы (будет использована версия имени с тире):

app/components/cart-contents.js

import Ember from 'ember';

export default Ember.Component.extend({
  shoppingCart: Ember.inject.service()
});

Поиск объекта фабрики

Чтобы извлечь фабрику с созданным объектом из запущенного приложения, можно вызвать метод lookup в экземпляре приложения. Этот метод принимает строковое значение, чтобы определить фабрику, и возвращает подходящий объект.

applicationInstance.lookup('factory-type:factory-name');

Экземпляр приложения передается hook'ам инициализатора экземпляра Ember и добавляется в качестве "владельца" каждого объекта, который был создан экземпляром приложения.

Использование экземпляра приложения в инициализаторе экземпляра

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

app/instance-initializers/logger.js

export function initialize(applicationInstance) {
  let logger = applicationInstance.lookup('logger:main');

  logger.log('Hello from the instance initializer!');
}

export default {
  name: 'logger',
  initialize: initialize
};

Получение экземпляра приложения по объекту фабрики

Ember.getOwner вернет экземпляр приложения, который «владеет» объектом. Это значит, что объекты фреймворка, например компоненты, хелперы и маршруты, могут использовать Ember.getOwner, чтобы выполнять поиск своего экземпляра приложения во время выполнения.

Например, этот компонент проигрывает песни с разных аудиосервисов на основе audioType песни.

app/components/play-audio.js

import Ember from 'ember';
const {
  Component,
  computed,
  getOwner
} = Ember;

// Usage:
//
//   {{play-audio song=song}}
//
export default Component.extend({
  audioService: computed('song.audioType', function() {
    let applicationInstance = getOwner(this);
    let audioType = this.get('song.audioType');
    return applicationInstance.lookup(`service:audio-${audioType}`);
  }),

  click() {
    let player = this.get('audioService');
    player.play(this.get('song.file'));
  }
});

Комментарии (0)

    Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.