DI Container на примере bottle.js

Это продолжение заметки Dependecy Injection

Если же развить паттерн Service Locator и представить себе центральный объект, в котором мы будем регистрировать все сервисы и зависимости и в конечном итоге получать центральный объект который и становиться нашей программой то мы получим паттерн DI Container. Здесь я продемонстрирую работу с библиотекой bottle.js как с примером реализации этого паттерна. Если почитать документацию то, как мне показалось, самым обобщённым и понятным подходом будет использование Service Factory

var bottlejs = require('bottlejs');

let bottle = bottlejs();

class PostDB {
  posts = [];
  
  save(post){
    this.posts.push(post);
  }
  
  getPosts(){
    return this.posts;
  }
  
  removePost(id){
    this.posts = this.posts.filter(post => post.id !== id);
  }
  
  getPostsById(id){
    return this.posts.filter(post => post.id === id);
  }
  
  getPostsByBlogId(id){
    return this.posts.filter(post => post.blogId === id);
  }
}

class Post{
  static id = 0;
  
  constructor(title, message, blogId){
    this.id = this.constructor.id++;
    this.title = title;
    this.message = message;
    this.blogId = blogId;
  }
}

class PostService{
  constructor(postDB, Post){
    this.postDB = postDB;
    this.Post = Post;
  }
  
  addPost(title, message, blogId = 0){
    const post = new this.Post(title, message, blogId);
    this.postDB.save(post);
    return post.id;
  }
  
  removePost(postId){
    this.postDB.removePost(postId);
  }
  
  getPosts(){
    return this.postDB.getPosts();
  }
  
  getPostsById(id){
    return postDB.getPostsById();
  }
  
  getPostsByBlogId(id){
    return postDB.getPostsByBlogId(id);
  }
}

bottle.factory('Post', (container) => Post);
bottle.service('postDB', PostDB); // Service это тот же Factory только возвращает объект переданного класса
bottle.factory('postService', (container) => new PostService(container.postDB, container.Post));

// А это Service вместо Service Factory где все аргументы после второго будут переданны в функцию-конструктор (второй аргумент):
// bottle.service('postService', PostService, 'postDB', 'Post'); 

bottle.container.postService.addPost('Title', 'Content');
bottle.container.postService.getPosts();
Важный момент это аргумент функции-параметра в методе factory, это объект bottle.container, который хранит в себе все зарегистрированные модули. Здесь в примере используюется и service который по сути является сахаром вокруг factory. Но я до конца не уверен в том правильно ли я храню класс Post, который я зарегистрировал с помощью Service Factory, но этот подход работает и выглядит нормально. Ну и дальше для удобства выносим bottle.container в отдельную переменную/константу:

const app = bottle.container;

app.postService.addPost('Title', 'Content');
app.postService.getPosts();
Добавлять лоигку работы с блогами я тут не буду, на мой взгляд и так всё понятно.

Ну и итог: При правильной модульности программы в ней появляется большое количество логических единиц (классов, объектов, функций) которые в конечном итоге надо собрать и связать с друг другом, все они будут взаимно использоваться в процессе работы и DI Container самая продвинутая техника (Ну, так говорят), при небольшом количестве этих самых подсистем можно и просто вручную создать нужные объекты, импортировать классы и использовать простой коструктор или сеттер, или же использовать Service Locator (глобальный объект со ссылками на все зависимости).

Комментарии

Популярные сообщения из этого блога

Загрузка CPU на Cisco Catalyst 4500 и Cat4k Mgmt LoPri

Пользовательские параметры в Zabbix (UserParameter)

Функторы в JavaScript