Dependency Injection

אנגולר עושה שימוש נרחב ב-Dependnecy Injection (הזרקת תלויות) על מנת לשמור על הקוד שלנו גמיש ומודלרי. כאשר אנחנו יוצרים אובייקט, controller לדוגמה, כל מה שאנחנו צריכים כמפתחים על מנת להשתמש ב- service מסויים זה פשוט לציין את שמו, אנגולר ידאג לאתר אותו, לייצר instance שלו, ו״להזריק״ אותו ל- controller.

בשביל לאתר את ה- service שאנחנו צריכים, אנגולר משתמש בשירות שנקרא injector$.

תפקידו של ה- injector הוא ״לחלץ״ את שמות ה-services שהועברו ל-constructor של ה- controller, לאתר אותם (אנגולר מנהל רישום של כל ה- services), ליצור instance ולהעביר אותו אל תוך האובייקט שלנו. ניתן לממש שירות שכזה באמצעות ה- design pattern שמכונה service locator.

דוגמה פשוטה של Service Locator
// example of a simple service locator class

function ServiceLocator () {

  this.services = {};

  this.register = function(name, constructorFn) {
    this.services[name] = constructorFn;
  };

  this.getService = function(name) {
    if (this.services[name]) {
      return new (this.services[name]);
    }
  };
};

// create an instane of the ServiceLocator
var locator = new ServiceLocator();

// register a service
locator.register('LogService', function () {  
    this.log = function (logMsg) {
        console.log(logMsg)
    }
})

// create another object, and ask for the service
var app = {  
    logService: locator.getService('LogService')
}

// use it!
app.logService.log('a messege!')  


נראה מוכר?

כאשר אנחנו משתמשים ב- API של ה- module על מנת ליצור service, אנחנו בעצם ״רושמים״ את ה- service כדי שנוכל לאתר אותו ולבקש instance שלו בשלב מאוחר יותר:

באנגולר, רישום של service נראה כך:
var app = angular.module('app', []);

// register a service
app.service('LogService', function () {  
    this.log = function (logMsg) {
        console.log(logMsg)
    }
})


איך אנגולר ״יודע״ לחלץ את השמות של ה-services?

ב-javascript ניתן לקרוא למתודה ()toString על פונקציה. מכאן, באמצעות מניפולציה פשוטה, נוכל לקבל את שמות ה- services כדי שנוכל לאתר אותם באמצעות ה- ServiceLocator:

// take a constructor function and extract the dependecies names

function extractDependeciesNames (constructorFn) {  
    var convertToSting = constructorFn.toString();
    return convertToSting.match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].split(',');            
}

// usage example: this is a simple controller that need 3 services

function Controller ($http, $log, aService) {}  

// the extractDependeciesNames extract and return the services names

var services = extractDependeciesNames(Controller);

// you can log it to see it in action
console.log(services)  


לסיכום

ב- angular, אנחנו ״רושמים״ services שנרצה להזריק בשלב מאוחר יותר בחיי האפליקציה.

לאחר מכן אנחנו יכולים ״לבקש״ את ה- services שרשמנו באמצעות ציון של השמות שלהם (פשוט strings)

מאחורי הקלעים, אנגולר מממש Design pattern בסגנון ה- ServiceLocator על מנת לאפשר את הפונקצינליות הזו.