UNPKG

servicelocatorjs

Version:
507 lines (380 loc) 12.1 kB
Service Locator =========== Implementation of the dependency injection approach using the `Service Locator` pattern. `Service Locator` is a dependency injection pattern. The `Service Locator` pattern does not describe how to instantiate the services. It describes a way to register **services** and **locate** them in a global point of access. A `Service Locator` should be able to locate a **service** using a central registry without knowing its concrete type. For example, it might use a string key which on request returns the object depending on code initialization. This allows you to replace the concrete implementation of the dependency without modifying the objects. ## Advantages: * option of adding extra attributes and methods using **mixin** pattern to all objects registered in `Service Locator`; * option of **lazy instantiation**; * un-registering instances; * un-registering objects; * applications can optimize themselves at run-time by selectively adding and removing items from the `Service Locator`; * large sections of a library or an application can be completely separated; the only link between them becomes the registry; * it solves the drawback of factories, allowing to manage the creation of objects automatically and centrally. ## Drawbacks: * your classes have an extra dependency on `Service Locator`; * you have to write additional code to add **service references** to the locator before your service objects use it; * objects placed in the **registry** are black boxed, this makes it harder to detect and recover from their errors, and may make the system as a whole less reliable; * sometimes **registry** can be a security vulnerability, because it allows outsiders to inject code into the application; * the source code has added complexity, this makes the source code more difficult to comprehend; * registry hides the object dependencies, causing errors when dependencies are missing; * **registry** makes the code harder to test, since all tests need to interact with the same global `Service Locator`; to set the fake dependencies of a service object under test. ### API #### Require in Node ```javascript var ServiceLocator = require("servicelocatorjs"); ``` ##### printLog (flag?: boolean) Takes _true/false_ values as a parameter. When _true_, writes information about events and channels into the console. ```javascript ServiceLocator.printLog(true); ``` ##### setMixin (objectWithMixins: Object) Takes an object as a parameter. The object contains a set of additional properties and/or methods, which have to contain all objects registered in `Service Locator`. ```javascript ServiceLocator.setMixin({ mixinMethod: function () {} }); ``` ##### getMixin (): Object Return current set mixins. ```javascript ServiceLocator.getMixin(); ``` ##### mixin(objectWithMixins?: Object): Object Set and/or return mixins. ```javascript ServiceLocator.mixin({ mixinMethod: function () {} }); ``` ##### register (serviceName: String, serviceObject: Function|Object, instantiate?: boolean, constructorArguments?: Array): boolean Registers an object **serviceObject** under the name **serviceName**. The flag **instantiate** shows whether lazy instantiation is required to request the object from `Service Locator`. By default **instantiate** is _true_. ```javascript ServiceLocator.register('serviceName', function (a, b) { this.a = a; this.b = b; }, true, [1, 2]); ``` ```javascript ServiceLocator.register('serviceName', { a: 1, b: 2 }); ``` ##### registerAll (arrayOfServices: Array): Array<String> Calls the **register** function for each element of **arrayOfServices**. Each element of the array must contain one of the **ID** or **id** properties for defining the object name, and **service/object/creator** for defining the object under registration. There is optional **instantiate**. ```javascript ServiceLocator.registerAll([ { /** * @constructor * @param {*} value */ creator: function (value) { this.prop = value; }, id: 'ServiceFive', instantiate: false }, { service: { prop: 'Some property' }, id: 'ServiceSix' } ]); ``` ##### get (serviceName: String): null|Object Returns the instance of a registered object with an indicated **serviceName** or creates a new one in case of lazy instantiation. ```javascript ServiceLocator.get('serviceName') ``` ##### instantiate (serviceName: String): boolean Instantiates service by name. ```javascript ServiceLocator.instantiate('serviceName') ``` ##### instantiateAll (filter?: Function) Instantiates and returns all registered objects. Can take the **filter** function as an argument. The **filter** function must return the logical value. In case filter is predefined, only the services that underwent the check will be instantiated. ```javascript ServiceLocator.instantiateAll(function (serviceName) { if (serviceName === 'ServiceName') { return false; } else { return true; } }) ``` ##### getAllInstantiate (): Array<String> Returns the array of instantiated service objects. ```javascript ServiceLocator.getAllInstantiate(); ``` ##### isRegistered (serviceName: String): boolean Checks whether the service is registered. ```javascript ServiceLocator.isRegistered('ServiceName'); ``` ##### isInstantiated (serviceName: String): boolean Checks whether the service is instantiated. ```javascript ServiceLocator.isInstantiated('ServiceName'); ``` ##### removeInstance (serviceName: String): boolean Deletes a service instance with an indicated **serviceName**. Returns _false_ in case the service with the indicated **serviceName** is not found or has no **instance**. This does not remove the service itself, but only its instances. ```javascript ServiceLocator.removeInstance('ServiceName'); ``` ##### unRegister (serviceName: Array|String, removeMixins?: boolean): null|Object Deletes a service named **serviceName** from `Service Locator` and returns its instance. The flag **removeMixins** points at the necessity to delete the added mixin properties. ```javascript ServiceLocator.unRegister('ServiceName', true); ``` ##### unRegisterAll(removeMixins?: boolean): Object Deletes all registered services from `Service Locator` and returns the array of their instances. The flag **removeMixin** points at the necessity to delete the added properties in the services that will be deleted. ```javascript ServiceLocator.unRegisterAll(true); ``` ## Example ###Creating a new instance: ####In runtime environment like node.js or io.js: ```javascript var ServiceLocator = require("servicelocatorjs"); ``` ####In the browser: ```javascript var ServiceLocator = window.ServiceLocator; ``` ####Print debug information in console: ```javascript ServiceLocator.printLog(true); ``` ####Create mixin for services: ```javascript var mixin = { /** * Set in service object new property <_state> for further use * @param {*} value */ setState: function (value) { this._state = value; }, /** * Get <_state> property from service object * @return {*} */ getState: function () { return '_state' in this ? this._state : undefined; }, /** * Get service object name * @return {String} */ getName: function () { return 'name' in this ? this.name : 'Service has no name!'; } }; ``` ####Set it for ServiceLocator: ```javascript ServiceLocator.setMixin(mixin); ``` ####Create constructors for services: ```javascript /** @constructor */ function ServiceOne() { this.name = 'ServiceOne'; // This property is not required. Made for example } /** @constructor */ function ServiceTwo() { this.name = 'ServiceTwo'; this.serviceFunction = function () { return 'Service number two function'; }; } /** * @param {*=} data * @constructor */ function ServiceThree(data) { // Service without <name> property this.data = data; } /** @constructor */ function ServiceFour() { this.name = 'ServiceFour'; } ``` ####Registering service objects in "ServiceLocator" ####With instantiation immediately after registration: ```javascript ServiceLocator.register('ServiceOne', ServiceOne, true); ``` In `Service Locator` registry its instance looks like this: ```javascript { __mixins: ["id", "setState", "getState", "getName"] getName: function, getState: function, id: "ServiceOne", name: "ServiceOne", setState: function } ``` ####With lazy instantiation: ```javascript ServiceLocator.register('ServiceTwo', ServiceTwo, false); ``` No instance, but there is a construction function: ```javascript { creator: function ServiceTwo(), name: "ServiceTwo", prototype: ServiceTwo } ``` ####Default immediate registration: ```javascript ServiceLocator.register('ServiceThree', ServiceThree, true, [{mydata: "example information"}]); ``` ```javascript ServiceLocator.get('ServiceThree').data; // {mydata: "example information"} ``` ####Create a service object by yourself: ```javascript var serviceFour = new ServiceFour; ``` ####Inject a previously created service object: ```javascript ServiceLocator.register(serviceFour.name, serviceFour); // or ServiceLocator.register('ServiceFour', serviceFour); ``` ####Get an instance of service: ```javascript var ONE = ServiceLocator.get('ServiceOne'); ``` ####Call a mixin method: ```javascript ONE.getName(); // "ServiceOne" ``` ####Call another mixin method: ```javascript ONE.setState("launched"); ``` ####Now call a mixin directly from "ServiceLocator": ```javascript ServiceLocator.get('ServiceOne').getState(); // "launched" ``` ####Service number three have mixin but have no "name" property: ```javascript ServiceLocator.get('ServiceThree').getName(); // → "Service has no name!" ``` ####Get currently instantiated services: ```javascript ServiceLocator.getAllInstantiate(); // ["ServiceOne", "ServiceThree", "ServiceFour"] ``` #### Instantiate all service objects but "ServiceTwo": ```javascript ServiceLocator.instantiateAll(function (serviceName) { if (serviceName === 'ServiceTwo') { return false; } else { return true; } }); ``` ####Now without exceptions: ```javascript ServiceLocator.instantiateAll(); // → "Instantiate: ServiceTwo" ``` ####Get currently instantiated services: ```javascript ServiceLocator.getAllInstantiate(); // ["ServiceOne", "ServiceTwo", "ServiceThree", "ServiceFour"] ``` ###Register multiple service objects Current state of registry inside `Service Locator`: ```javascript { "ServiceFour": ▸Object "ServiceOne": ▸Object "ServiceThree": ▸Object "ServiceTwo": ▸Object } ``` ####Previosly set state: ```javascript ServiceLocator.get('ServiceOne').getState(); // "launched" ``` ####Remove the instance, but keep the service. This removes any non-default set data in the service object: ```javascript ServiceLocator.removeInstance('ServiceOne'); ``` ####"ServiceLocator" will instantiate a new instance of the service object: ```javascript ServiceLocator.get('ServiceOne').getState(); // undefined // → "Instantiate: ServiceOne" ``` As you can see, previously saved data won't be brought back. ####Deletes a service from "ServiceLocator" and returns its instance: ```javascript var unRegisteredService = ServiceLocator.unRegister('ServiceFive'); ``` ```javascript { __mixins: ["id", "setState", "getState", "getName"], id: "ServiceFive", getState: ▸Function, prop: ▸Array, setState: ▸Function, } ``` ####Same as above, but without mixins: ```javascript var unRegisteredServiceWithoutMixins = ServiceLocator.unRegister('ServiceFive', true); ``` Any mentions were removed, so: ```javascript ServiceLocator.get('ServiceFive'); // null // → "Service is not registered: ServiceFive" ``` ####Delete all registered services from "ServiceLocator" and return an array of their instances: ```javascript ServiceLocator.unRegisterAll(); ``` ```javascript { "ServiceFive": ▸Object, "ServiceFour": ▸Object, "ServiceOne": ▸Object, "ServiceSix": ▸Object, "ServiceThree": ▸Object, "ServiceTwo": ▸Object, } ``` ####Same as above, but returned objects have their mixins removed: ```javascript ServiceLocator.unRegisterAll(true); ```