owja / ioc

:unicorn: lightweight (<1kb) inversion of control javascript library for dependency injection written in typescript
MIT License
287 stars 13 forks source link

Idea for the docs? #31

Closed levibostian closed 5 years ago

levibostian commented 5 years ago

This is how I personally like to use this library in my project:

import {Container} from "@owja/ioc";
const container = new Container();

const BAR = Symbol.for("bar")
const FOO = Symbol.for("foo")

interface Foo {
  doThing(): void
  tellBarToDoThing(): void 
}

class AppFoo implements Foo {
  constructor(private bar: Bar = container.get(BAR)) {}

  doThing(): void {
    console.log('foo do thing')
  }

  tellBarToDoThing() {
    this.bar.doThing()
  }
}

container.bind<Foo>(FOO).to(AppFoo)
interface Bar {
  doThing(): void 
  tellFooToDoThing(): void
}

class AppBar implements Bar {
  constructor(private foo: Foo = container.get(FOO)) {}
  doThing() {
    console.log('bar do thing')
  }

  tellFooToDoThing() {
    this.foo.doThing()
  }
}

// You don't need to use `toFactory()` below, but here just for example sake. 
container.bind<Bar>(BAR).toFactory(() => { return new AppBar() }).inSingletonScope() 

let foo: Foo = container.get(FOO)
foo.doThing()

let bar: Bar = container.get(BAR)
bar.doThing()

The code to look at here is the constructors of the classes. This is how I like to resolve the dependencies for my classes instead of wire() or resolve() that the readme suggests.

Wanted to see what you thought. Do you see anything wrong with this? Do you see it as a value add to add this technique to the readme as another option to resolving dependencies?

hbroer commented 5 years ago

Hi, sorry for the late answer. :)

class AppFoo implements Foo {
  constructor(private bar: Bar = container.get(Symbol.for("bar")) {}
  ...
}

class AppBar implements Bar {
  constructor(private foo: Foo = container.get(Symbol.for("foo")) {}
  ...
}

Are you sure that this example will work? The problem is that this way you can't have a circular dependency because you resolve the dependency before the constructor is finished (inside of the constructor). That's why wire() and resolve() exist. They resolve the dependency after construction of the class, just at that moment when the dependency is needed (on property access).

I would recommend to use wire:

class Example {    
    readonly foo!: FooInterface;
    constructor() {
        wire(this, "foo", Symbol.for("foo"));
    }
    ...
}

or resolve:

class Example {    
    constructor(private foo = resolve<FooInterface>(Symbol.for("foo"))) {}
    ...
}

This way circular dependencies will work (if you do not access them inside of the constructor). The difference between this two is that foo is a property getter in example one this.foo and in example two it is a function this.foo()

levibostian commented 5 years ago

Ah, I never thought about that. I have been using my container.get() technique in my projects but I suppose I have not encountered a scenario with circular dependencies yet. I suppose it works for now until there is a problem!

I tried the example from my code and no, it does not work as you expected. Stackoverflow within owja/ioc.

Thanks for the help explaining this. Looks like we will stick with the owja/ioc API that exists already.