| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]--> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <meta name="generator" content="Asciidoctor 1.5.7.1"> |
| <title>Dependency Injection in N4JS</title> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> |
| <!-- ************* Meta ************* --> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> |
| |
| <!-- ************* OpenGraph ************--> |
| <meta name="description" content="The Eclipse N4JS language and its IDE enable high-quality JavaScript development for large Node.js projects."> |
| |
| <meta property="og:site_name" content="Eclipse N4JS"/> |
| <meta property="og:title" content="Eclipse N4JS Language and IDE"/> |
| <meta property="og:url" content="https://numberfour.github.io/n4js"/> |
| <meta property="og:description" content="The Eclipse N4JS language and its IDE enable high-quality JavaScript development for large Node.js projects."/> |
| <meta property="og:image" content="../images/n4js.png"> |
| |
| <!-- ************* Favicon ************--> |
| <link rel="icon" href="../images/favicon.ico" /> |
| <link rel="icon" type="image/png" href="../images/favicon-32x32.png" sizes="32x32" /> |
| <link rel="icon" type="image/png" href="../images/favicon-16x16.png" sizes="16x16" /> |
| |
| <!-- ************* Back-to-top JQuery ************* --> |
| <script src="https://code.jquery.com/jquery-1.12.4.js"></script> |
| <script src="https://code.jquery.com/ui/1.12.0/jquery-ui.js"></script> |
| |
| <!-- ************* Prism.js Syntax Highlighter ******--> |
| <link href="../styles/prism.min.css" rel="stylesheet"/> |
| <script src="../scripts/prism.js"></script> |
| |
| <!-- ************* Styles ************* --> |
| |
| <link rel="stylesheet" type="text/css" href="../styles/n4js-adoc.css"> |
| |
| <!-- ****************** NavBar ****************** --> |
| <div id="menubar"> |
| <div class="banner"> |
| <a href="../index.html"><img id="logo" src="../images/n4js-logo.png" alt="Eclipse N4JS Language and IDE"></a> |
| </div> |
| <ul> |
| <li><a href="../downloads.html"></i>Download</a></li> |
| <li><a href="../community.html"></i>Community</a></li> |
| <li><a href="../userguides/index.html">Documentation</a></li> |
| </ul> |
| </div> |
| <button id="tocbutton">TOC</button> |
| </head> |
| <body class="book"> |
| <div id="header"> |
| </div> |
| <div id="content"> |
| <h1 id="_dependency_injection_in_n4js" class="sect0"><a class="link" href="#_dependency_injection_in_n4js">Dependency Injection in N4JS</a></h1> |
| <div class="openblock partintro"> |
| <div class="content"> |
| <div class="paragraph"> |
| <div class="title">Dependency Injection in N4JS</div> |
| <p>Dependency injection (DI) is a concept that allows for configuring dependencies |
| between classes at a central location. Instead of passing dependencies from class to class, N4JS' built-in |
| DI support does this automatically.</p> |
| </div> |
| <div class="paragraph"> |
| <p>This makes the user code much cleaner, easier to maintain and also improves |
| its testability. N4JS DI framework follows Java JSR-330/Google Guice, making its usage easy for Java developers.</p> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_application_object_graph"><a class="link" href="#_application_object_graph">Application Object Graph</a></h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>In object oriented languages, applications are composed from objects that interact with each other. Instances |
| of those objects need to be created and wired together on application startup to create a so-called object |
| graph of the application. While it’s possible to manually create this object graph, it quickly becomes |
| complicated. This is especially so if we want flexibility and reconfigurability to be long-lasting features |
| of our application.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Solutions for manually wiring the object graph come with their distinct disadvantages:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Hard coding dependencies makes code inflexible and complicate testing.</p> |
| </li> |
| <li> |
| <p>Passing dependencies to constructors bloats the constructors.</p> |
| </li> |
| <li> |
| <p>Using factories requires passing the factory and also bloats the code.</p> |
| </li> |
| </ul> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_dependency_injection_di"><a class="link" href="#_dependency_injection_di">Dependency Injection (DI)</a></h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>Dependency injection (DI) and DI frameworks aim to help with issues described before, specifically with the |
| following:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>The object graph is created automatically which removes burden of writing object factories.</p> |
| </li> |
| <li> |
| <p>Injection of the created instances is done behind the scenes where needed, which separates object-creation |
| from object-usage and keeps constructors simple.</p> |
| </li> |
| <li> |
| <p>The application’s configuration can be changed without changing its components.</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>N4JS provides built-in support for dependency injection using a lightweight syntax with annotation similar to |
| Java <a href="https://jcp.org/en/jsr/detail?id=330">JSR-330</a> / <a href="https://github.com/google/guice">Google Guice</a>. |
| The N4JS testing framework also supports dependency injection which allows for special test settings in order to test components individually.</p> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_example"><a class="link" href="#_example">Example</a></h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>In the following example, two versions of a simple weather application are implemented. Both versions use a |
| module WeatherEngine which returns the temperature for a given city. For this example, we use a timeout to |
| emulate a real request to a weather server:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-n4js" data-lang="n4js">export public class WeatherEngine { |
| data = [ {city: 'Berlin', temp: 5}, {city: 'Hamburg', temp: 15}, {city: 'Palo Alto', temp: 10} ]; |
| |
| public temperature(city: string): Promise&lt;number, ?&gt; { |
| return new Promise&lt;number, any&gt;( |
| (cb: {function(number)}) =&gt; { |
| setTimeout(() =&gt; cb(this.data.find(e =&gt; e.city == city).temp) , Math.random() * 2000); |
| }); |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>In order to keep the examples as small as possible, in the non-DI version no manual wiring of the dependencies |
| is used. The components are instead set up by initializing the fields directly.</p> |
| </div> |
| <table class="tableblock frame-all grid-all stretch"> |
| <colgroup> |
| <col style="width: 50%;"> |
| <col style="width: 50%;"> |
| </colgroup> |
| <tbody> |
| <tr> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><strong>Without Dependency-Injection</strong></p></td> |
| <td class="tableblock halign-left valign-top"><p class="tableblock"><strong>With Dependency-Injection</strong></p></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock"> |
| <div class="title">WeatherApp.n4js</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-n4js" data-lang="n4js">import { WeatherEngine } from 'WeatherEngine'; |
| |
| export class WeatherApp { |
| private engine: WeatherEngine = new WeatherEngine(); |
| |
| async printTemp(city: string): string { |
| return city + ': ' + (await this.engine.temperature(city)); |
| } |
| } |
| |
| |
| export class Server { |
| weatherApp: WeatherApp = new WeatherApp(); |
| |
| async run() { |
| for (var s of ['Berlin', 'Hamburg', 'Palo Alto']) { |
| console.log(await this.weatherApp.printTemp(s)); |
| } |
| } |
| }</code></pre> |
| </div> |
| </div></div></td> |
| <td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock"> |
| <div class="title">WeatherAppDI.n4js</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-n4js" data-lang="n4js">import { WeatherEngine } from 'WeatherEngine'; |
| |
| export class WeatherApp { |
| @Inject private engine: WeatherEngine; |
| |
| async printTemp(city: string): string { |
| return city + ': ' + (await this.engine.temperature(city)); |
| } |
| } |
| |
| @GenerateInjector |
| export class Server { |
| @Inject weatherApp: WeatherApp; |
| |
| async run() { |
| for (var s of ['Berlin', 'Hamburg', 'Palo Alto']) { |
| console.log(await this.weatherApp.printTemp(s)); |
| } |
| } |
| }</code></pre> |
| </div> |
| </div></div></td> |
| </tr> |
| <tr> |
| <td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock"> |
| <div class="title">Starter.n4js</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-n4js" data-lang="n4js">import { Server } from 'WeatherApp' |
| |
| |
| var server = new Server(); |
| server.run();</code></pre> |
| </div> |
| </div></div></td> |
| <td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock"> |
| <div class="title">StarterDI.n4js</div> |
| <div class="content"> |
| <pre class="highlight"><code class="language-n4js" data-lang="n4js">import { Server } from 'WeatherAppDI'; |
| import { N4Injector } from 'n4js/lang/N4Injector'; |
| |
| var server = N4Injector.of(Server).create(Server); |
| server.run();</code></pre> |
| </div> |
| </div></div></td> |
| </tr> |
| </tbody> |
| </table> |
| <div class="paragraph"> |
| <p>The changes are only minimal: Instead of creating the field instances directly, they are annotated |
| with <code>@Inject</code>. This should be familiar to Java programmers having used Google Guice.</p> |
| </div> |
| <div class="paragraph"> |
| <p>An interesting aspect of dependency injection is how to set up the injector. In N4JS, the |
| annotation <code>@GenerateInjector</code> is used in order mark a class as a dependency |
| injection component. In other words, to associate an injector with the class. Running the server now |
| requires slightly different instantiation. Instead of constructing the server with <code>new</code>, |
| the built-in class <code>N4Injector</code> is used to create the first instance.</p> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_application_reconfigurability"><a class="link" href="#_application_reconfigurability">Application Reconfigurability</a></h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>A very useful quality of DI is its flexibility. This is particularly beneficial during testing. Let’s |
| write a test class for our <code>WeatherApp</code>. We do not want to wait an arbitrary amount of |
| seconds to receive the results of our test, we want to use a special version of the <code>WeatherEngine</code> |
| which immediately returns a value. Let’s have a look at the test module:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="highlight"><code class="language-n4js" data-lang="n4js">import { WeatherApp } from 'WeatherAppDI'; |
| import { WeatherEngine } from 'WeatherEngine'; |
| import { Assert } from 'n4/mangel/assert/Assert'; |
| |
| class WeatherEngineMock extends WeatherEngine { <i class="conum" data-value="1"></i><b>(1)</b> |
| @Override |
| public async temperature(city: string): number { |
| return 1; |
| } |
| } |
| |
| @Binder <i class="conum" data-value="2"></i><b>(2)</b> |
| @Bind(WeatherEngine, WeatherEngineMock) |
| class WeatherAppTestConfig{ } |
| |
| @GenerateInjector() <i class="conum" data-value="3"></i><b>(3)</b> |
| @UseBinder(WeatherAppTestConfig) <i class="conum" data-value="4"></i><b>(4)</b> |
| export class WeatherAppTest { |
| @Inject weatherApp: WeatherApp; |
| |
| @Test public async test() { |
| Assert.equal(await this.weatherApp.printTemp('Berlin'), 'Berlin: 1'); |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="colist arabic"> |
| <table> |
| <tr> |
| <td><i class="conum" data-value="1"></i><b>1</b></td> |
| <td>A mock engine must be written.</td> |
| </tr> |
| <tr> |
| <td><i class="conum" data-value="2"></i><b>2</b></td> |
| <td>Followed by a "binder" which is a configuration telling the injector what type has to be used to instantiate objects. By default, the |
| injector uses the same class as referenced in the code. We change this and bind the mock engine to the |
| real engine.</td> |
| </tr> |
| <tr> |
| <td><i class="conum" data-value="3"></i><b>3</b></td> |
| <td>As the N4JS test framework already supports DI, we can declare the test as a new dependency injection. |
| component.</td> |
| </tr> |
| <tr> |
| <td><i class="conum" data-value="4"></i><b>4</b></td> |
| <td>Specific test configuration. The important point is that the |
| class <code>WeatherApp</code> now gets the <code>WeatherEngineMock</code> injected.</td> |
| </tr> |
| </table> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_advanced_features"><a class="link" href="#_advanced_features">Advanced features</a></h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>Specific advantages and extended DI features are discussed in greater detail in the N4JS language |
| spec. Some of the most notable features are:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Built-in pseudo-scope via <code>@Singleton</code>.</p> |
| </li> |
| <li> |
| <p>Possibility of nesting injectors via <code>@WithParentInjector</code>.</p> |
| </li> |
| <li> |
| <p>Built-in <code>Provider</code> type and possibility to create custom providers via <code>@Provides</code> to dynamically create instances.</p> |
| </li> |
| <li> |
| <p>Automatic resolution of dependency cycles.</p> |
| </li> |
| </ul> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div id="footer"> |
| <div id="footer-text"> |
| </div> |
| </div> |
| <div class="Grid social" style="color:#d5dfea"> |
| <div class="Cell Cell--2-12 m-Cell--withMargin"> |
| <h2>Quick Links</h2> |
| <ul> |
| <li><a href="../downloads.html">Download</a></li> |
| <li><a href="../userguides/index.html">Documentation</a></li> |
| <li><a href="https://github.com/eclipse/n4js/">Source</a></li> |
| <li><a href="https://github.com/eclipse/n4js/issues">Issues</a></li> |
| </ul> |
| </div> |
| <div class="Cell Cell--2-12 m-Cell--withMargin"> |
| <br/><br/> |
| <ul> |
| <li><a href="https://www.eclipse.org/forums/index.php/f/365/">Forum</a></li> |
| <li><a href="http://n4js.blogspot.de/">Blog</a></li> |
| <li><a href="https://dev.eclipse.org/mailman/listinfo/n4js-dev">Mailing List</a></li> |
| <li><a href="https://projects.eclipse.org/projects/technology.n4js">Eclipse Project Page</a></li> |
| <li><a href="https://twitter.com/n4jsdev">Tweets by n4jsdev</a></li> |
| </ul> |
| </div> |
| <div class="Cell Cell--2-12 m-Cell--withMargin"> |
| <br/><br/> |
| <ul> |
| <li><a href="http://www.eclipse.org/">Eclipse Home</a></li> |
| <li><a href="http://www.eclipse.org/legal/privacy.php">Privacy Policy</a></li> |
| <li><a href="http://www.eclipse.org/legal/termsofuse.php">Terms of Use</a></li> |
| <li><a href="http://www.eclipse.org/legal/copyright.php">Copyright Agent</a></li> |
| <li><a href="http://www.eclipse.org/legal/">Legal</a></li> |
| </ul> |
| </div> |
| <div style="clear: both; height: 0; overflow: hidden;"></div> |
| </div> |
| |
| <script> |
| // Toggle the table of contents |
| $( "button#tocbutton" ).click(function() { |
| if ($("#tocbutton").css('right') == '25px') { |
| $( "#tocbutton" ).animate({right: '215px'},"slow"); |
| $( "#toc.toc2" ).animate({right: '0'},"slow"); |
| } |
| else { |
| $( "#tocbutton" ).animate({right: '25px'},"slow"); |
| $( "#toc.toc2" ).animate({right: '-13rem'},"slow"); |
| } |
| }); |
| </script> |
| |
| <script type="text/javascript"> |
| // Create a back to top button |
| $('body').prepend('<a href="#" class="back-to-top">Back to Top</a>'); |
| var amountScrolled = 300; |
| $(window).scroll(function() { |
| if ( $(window).scrollTop() > amountScrolled ) { |
| $('a.back-to-top').fadeIn('slow'); |
| } else { |
| $('a.back-to-top').fadeOut('slow'); |
| } |
| }); |
| $('a.back-to-top, a.simple-back-to-top').click(function() { |
| $('html, body').animate({ |
| scrollTop: 0 |
| }, 700); |
| return false; |
| }); |
| </script> |
| </body> |
| </html> |