A reflection based (DI)Dependency Injector component for golang project.
- Provide: struct pointer or function
- ProvideAs: struct pointer or function as interface type
- Find: dependency inject for struct pointer or interface
- Resolve: dependency inject for struct fields
- Invoke: use dependencies as parameter on invoke method
- support recursive construct dependencies on find/resolve/invoke
installation
go install github.com/shyandsy/di
please check unit test code
create a container
c := di.NewContainer()add a dependency which is a pointer of struct to container
// object: dependency object, should be a pointer of struct
Provide(object interface{}) errorusage:
cat1 := &Cat{Name: "A"}
dog1 := &Dog{Name: "B"}
// Provide
err := c.Provide(cat1)
err = c.Provide(dog1)
cat2 := &Cat{}
dog2 := &Dog{}
// Find:cat2.Name = "A"
err = c.Find(cat2)
// Find:dog2.Name = "B"
err = c.Find(dog2)we wanna programming to the interface rather than implementation. ProvideAs is what you need to add dependency as an interface to the container.
// object: dependency object, should be a pointer of struct
// target: specify the interface type, should be a pointer of interface, egg: (*Pet)(nil)
ProvideAs(object interface{}, target interface{}) errorusage:
type Animal interface {
GetName() string
}
type Pet interface {
Animal
}
cat1 := NewPetCat("cat")
animalCat := NewAnimalCat("ccc")
// Provide and ProvideAs
err = c.ProvideAs(cat1, (*Pet)(nil))
err = c.ProvideAs(animalCat, (*Animal)(nil))
var P Pet
var A Animal
// Find interface
// P.GetName(): "cat"
err = c.Find(&P)
// Find interface
// A.GetName(): "ccc"
err = c.Find(&A) find is to fetch a single dependency object
// object: to fetch the dependency, should be a pointer of struct, or a pointer of interface
Find(object interface{}) errorusage
c := NewContainer()
var s Animal
// inject Animal interface
err = c.ProvideAs(&Cat{Name: "A"}, (*Animal)(nil))
assert.Nil(t, err)
// inject pointer of struct
err := c.Provide(&Cat{Name: "A"})
assert.Nil(t, err)
// use case 1: fetch pointer of struct
a := Cat{}
err = c.Find(&a)
assert.Nil(t, err)
assert.Equal(t, a.GetName(), "A")
// use case 2: fetch interface
err = c.Find(&s)
assert.Nil(t, err)
assert.True(t, s != nil)
assert.Equal(t, s.GetName(), "A")sometimes, we wanna simple inject dependencies based on struct field
- field type must be pointer of struct, or interface
- field must have
injecttag - field must be writable which is
// object: struct we wanna inject dependencies, object must be pointer of struct
Resolve(object interface{}) errorusage:
type Cat struct {
Name string
}
type temp struct {
Cat *Cat `inject:""`
Pet Pet `inject:""`
Animal Animal `inject:""`
}
c := NewContainer()
// Provide dependency
err := c.Provide(&Cat{Name: "A"})
err = c.ProvideAs(&Cat{Name: "B"}, (*Pet)(nil))
err = c.ProvideAs(NewPetCat("C"), (*Animal)(nil))
// Resolve struct field
s := &temp{}
err = c.Resolve(s)
assert.Nil(t, err)
/*
now we have:
s.Cat.GetName(): "A"
s.Pet.GetName(): "B"
s.Animal.GetName(): "C"
*/call a function with dependencies injection
notice: the parameters of function called by Invoke must be pointer of struct, or interface
// f: the function we wanna call
Invoke(f interface{}) errorusage
func PrintCatAndAnimal(cat1 *Cat, cat2 *Cat, animal Animal) {
fmt.Println(fmt.Sprintf("cat1:%s\ncat2:%s\nanimal:%s\n", cat1.Name, cat2.Name, animal.GetName()))
}
func PrintCatAndAnimalRecursive(s *temp2) {
fmt.Println(fmt.Sprintf("cat1:%s\ncat2:%s\nanimal:%s\n", s.Temp.Cat.Name, s.Temp.Pet.GetName(), s.Temp.Animal.GetName()))
}
c := NewContainer()
cat := &Cat{Name: "A"}
animal := NewAnimalCat("B")
// add dependencies
err := c.Provide(cat)
assert.Nil(t, err)
err = c.ProvideAs(animal, (*Animal)(nil))
assert.Nil(t, err)
// invoke
err = c.Invoke(PrintCat)
assert.NotNil(t, err)
// invoke
err = c.Invoke(PrintCatWithInvalidParameter)
assert.NotNil(t, err)
// invoke
err = c.Invoke(PrintCatPointer)
assert.Nil(t, err)
// invoke
err = c.Invoke(PrintCatAndAnimal)
assert.Nil(t, err)
}please check unit test