【问题标题】:IOC for TypeScript用于 TypeScript 的 IOC
【发布时间】:2012-09-29 12:38:24
【问题描述】:

现在有了 TypeScript,我们在 JavaScript 中有了静态分析和许多 OOP 功能。 所以现在是时候在客户端逻辑中进行更好的单元测试了,而且我们需要 IOC 容器来进行依赖注入,以使代码更具可测试性......

那么,有人已经体验过这个主题,或者可能知道可以移植到 TypeScript 的 typescript 或 JavaScript 框架的库吗?

【问题讨论】:

  • 在运行时,没有 TypeScript;只有Javascript。所以它们在运行时不是静态类型系统,而对于 IoC,我们需要一个运行时类型系统。
  • 是的,我知道,但是运行时也没有 c#|Delphi 等,并且已经有使用 SourceMap (stackoverflow.com/questions/12711826/…) 调试 TypeScript 的方法。
  • 确实如此,但在 .NET 中,我们可以通过使用反射来确定构造函数需要哪些类型。此类功能不适用于 javascript。

标签: javascript dependency-injection inversion-of-control typescript


【解决方案1】:

目前,您可以在没有 IOC 部分的 JavaScript 中使用依赖注入。是否编写“手动”解析器、工厂或任何您喜欢的 DI 模式取决于您。

当采用 ECMAScript 6 标准时,可能会使 IOC 的概念在 JavaScript 中成为可能。

【讨论】:

    【解决方案2】:

    我们一直在使用一个简单的依赖注入容器,它使用 AMD 定义/要求 - 类似语法。最初的实现是在 TypeScript 中,尽管下面的博客文章用普通的旧 JavaScript 呈现它。

    http://blog.coolmuse.com/2012/11/11/a-simple-javascript-dependency-injection-container/

    定义依赖关系非常简单,不需要一堆配置,并且支持类似于requirejs的循环依赖解析。

    这是一个简单的例子:

    // create the kernel
    var kernel = new ServiceKernel();
    
    // define service1
    kernel.define("service1", function() {
    
        // service1 has a function named foo
        return {
            foo: function () { return "foo"; }
        }
    
    });
    
    // define service2, which depends on service1
    kernel.define("service2", ["service1"], function(service1) {
    
        // service2 has a function named foobar
        return {
            foobar : function() { return service1.foo() + "bar"; }
        }
    
    });
    
    // get service2 instance 
    var service2 = kernel.require("service2");
    service2.foobar();  // returns "foobar"
    
    // get both service1 and service2 instances
    kernel.require(["service1", "service2"], function(service1, service2) {
    
        alert(service1.foo() + service2.foobar()); // displays foofoobar
    
    });
    

    【讨论】:

      【解决方案3】:

      我开发了一个名为 InversifyJS 的 IoC 容器,它具有上下文绑定等高级依赖注入功能。

      您需要遵循3个基本步骤才能使用它:

      1。添加注释

      注解 API 基于 Angular 2.0:

      import { injectable, inject } from "inversify";
      
      @injectable()
      class Katana implements IKatana {
          public hit() {
              return "cut!";
          }
      }
      
      @injectable()
      class Shuriken implements IShuriken {
          public throw() {
              return "hit!";
          }
      }
      
      @injectable()
      class Ninja implements INinja {
      
          private _katana: IKatana;
          private _shuriken: IShuriken;
      
          public constructor(
              @inject("IKatana") katana: IKatana,
              @inject("IShuriken") shuriken: IShuriken
          ) {
              this._katana = katana;
              this._shuriken = shuriken;
          }
      
          public fight() { return this._katana.hit(); };
          public sneak() { return this._shuriken.throw(); };
      
      }
      

      2。声明绑定

      绑定API基于Ninject:

      import { Kernel } from "inversify";
      
      import { Ninja } from "./entities/ninja";
      import { Katana } from "./entities/katana";
      import { Shuriken} from "./entities/shuriken";
      
      var kernel = new Kernel();
      kernel.bind<INinja>("INinja").to(Ninja);
      kernel.bind<IKatana>("IKatana").to(Katana);
      kernel.bind<IShuriken>("IShuriken").to(Shuriken);
      
      export default kernel;
      

      3。解决依赖关系

      解析API基于Ninject:

      import kernel = from "./inversify.config";
      
      var ninja = kernel.get<INinja>("INinja");
      
      expect(ninja.fight()).eql("cut!"); // true
      expect(ninja.sneak()).eql("hit!"); // true
      

      最新版本 (2.0.0) 支持许多用例:

      • 内核模块
      • 内核中间件
      • 使用类、字符串文字或符号作为依赖标识符
      • 注入常量值
      • 注入类构造函数
      • 注入工厂
      • 汽车厂
      • 提供程序注入(异步工厂)
      • 激活处理程序(用于注入代理)
      • 多次注射
      • 标记绑定
      • 自定义标签装饰器
      • 命名绑定
      • 上下文绑定
      • 友好的例外(例如循环依赖)

      您可以通过https://github.com/inversify/InversifyJS了解更多信息

      【讨论】:

      【解决方案4】:

      我已经为 typescript 创建了 DI 库 - huject

      https://github.com/asvetliakov/huject

      例子:

      import {Container, FactoryMethod, ConstructorInject, Inject} from 'huject';
      class FirstService {
         public constructor(param: number) {}
      }
      class SecondService {
      }
      
      @ConstructorInject
      class MyController {
          @Inject
          public service: FirstService;
      
          public second: SecondService;
          public constructor(service: SecondService) {
              this.second = service;
          }
          ...
      }
      container.setAllowUnregisteredResolving(true);
      container.register(FirstService, [10]); // Register constructor arguments
      
      // FirstService and SecondService will be resolved for container instance
      let controller = container.resolve(MyController);
      

      TypeScript 接口存在问题,但我有 2 个解决方法(使用抽象类或简单类作为接口)

      【讨论】:

      • 谢谢,哦,伙计,我不敢相信没有人创建了一个可以很好地与 typescript 配合使用的适当的依赖注入系统(很像 Angualr2 DI)。感谢您创建它,它很可爱,已将其添加到我的项目中
      • 这就是我要找的东西!反转被夸大了
      【解决方案5】:

      或者,您可以不使用任何框架,而是使用类作为容器,将对象工厂作为属性。然后,您可以在测试中继承此类并更改工厂。这种方法是类型安全的,不需要任何装饰器,只需注册类。

      class B {
          echo() {
              alert('test');
          }   
      }
      
      class A {
          constructor(private b: B) {
              b.echo();
          }
      }
      
      class Container {
          A = () => new A(this.B());
          B = singleton(() => new B()); 
      }
      
      var c = new Container();
      var a = c.A();
      
      // singleton helper:
      
      function memoize<T>(factory: () => T): () => T  {
          var memo: T = null;
          return function () {
              if(!memo) {
                  memo = factory();
              }
              return memo;
          };
      }
      
      var singleton = memoize;
      

      【讨论】:

        【解决方案6】:

        结帐https://github.com/typestack/typedi

        这样的事情是可能的:

        import "reflect-metadata";
        import {Service, Container} from "typedi";
        
        @Service()
        class SomeClass {
        
            someMethod() {
            }
        
        }
        
        let someClass = Container.get(SomeClass);
        someClass.someMethod();
        

        【讨论】:

          【解决方案7】:

          我曾经在中小型项目中使用TSyringe。它工作得很好,但我的结论是我不需要在 JavaScript / TypeScript 中进行依赖注入。

          由于 JavaScript 是一种动态语言,所有实现都可以在运行时更改。如果您只想注入存根对象进行单元测试,请使用SinonJS 或类似工具。

          为什么:

          • 它会给您的代码带来不必要的复杂性和冗长的样板代码。
          • 一切都应该写成 Class,因此 TypeScript 无法为某些方法推断正确的类型,这意味着您应该手动为它们键入参数。
          • 大多数依赖注入框架依赖于 TypeScript 装饰器,编译速度很慢。该功能仍处于试验阶段(可能会随着时间的推移而改变)

          【讨论】:

            猜你喜欢
            • 2016-06-03
            • 2016-07-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-04-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多