Skip to content

Commit 6b5ffa5

Browse files
CililingPrzemyslaw Materna
andauthored
Add standalone container (#18)
* Delete container var. Use type Container instead. * Sort methods in container. * Fix tests. * Move container definition to the pkg directory. Create default container instance. * Update readme. Co-authored-by: Przemyslaw Materna <pmaterna@opera.com>
1 parent 8d4f50f commit 6b5ffa5

File tree

4 files changed

+191
-136
lines changed

4 files changed

+191
-136
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,18 @@ container.Singleton(func(c Config) Database {
133133

134134
Notice: You can only resolve the dependencies in a binding resolver function that has already bound.
135135

136+
### Standalone instance
137+
138+
Container works without any initialization keeping your bindings in the default instance. Sometimes you may want to create a standalone instance for a part of application. If so, create a new instance:
139+
140+
```go
141+
c := container.NewContainer() // returns container.Container
142+
c.Singleton(binding)
143+
c.Make(&resolver)
144+
```
145+
146+
The rest stays the same. The default container is still available.
147+
136148
### Usage Tips
137149

138150
#### Performance

container.go

Lines changed: 14 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -3,122 +3,32 @@
33
package container
44

55
import (
6-
"reflect"
6+
internal "github.com/golobby/container/pkg/container"
77
)
88

9-
// invoke will call the given function and return its returned value.
10-
// It only works for functions that return a single value.
11-
func invoke(function interface{}) interface{} {
12-
return reflect.ValueOf(function).Call(arguments(function))[0].Interface()
9+
func NewContainer() internal.Container {
10+
return make(internal.Container)
1311
}
1412

15-
// binding keeps a binding resolver and instance (for singleton bindings).
16-
type binding struct {
17-
resolver interface{} // resolver function
18-
instance interface{} // instance stored for singleton bindings
19-
}
20-
21-
// resolve will return the concrete of related abstraction.
22-
func (b binding) resolve() interface{} {
23-
if b.instance != nil {
24-
return b.instance
25-
}
26-
27-
return invoke(b.resolver)
28-
}
29-
30-
// container is the IoC container that will keep all of the bindings.
31-
var container = map[reflect.Type]binding{}
32-
33-
// bind will map an abstraction to a concrete and set instance if it's a singleton binding.
34-
func bind(resolver interface{}, singleton bool) {
35-
resolverTypeOf := reflect.TypeOf(resolver)
36-
if resolverTypeOf.Kind() != reflect.Func {
37-
panic("the resolver must be a function")
38-
}
39-
40-
for i := 0; i < resolverTypeOf.NumOut(); i++ {
41-
var instance interface{}
42-
if singleton {
43-
instance = invoke(resolver)
44-
}
45-
46-
container[resolverTypeOf.Out(i)] = binding{
47-
resolver: resolver,
48-
instance: instance,
49-
}
50-
}
51-
}
52-
53-
// arguments will return resolved arguments of the given function.
54-
func arguments(function interface{}) []reflect.Value {
55-
functionTypeOf := reflect.TypeOf(function)
56-
argumentsCount := functionTypeOf.NumIn()
57-
arguments := make([]reflect.Value, argumentsCount)
58-
59-
for i := 0; i < argumentsCount; i++ {
60-
abstraction := functionTypeOf.In(i)
13+
// A default instance for container
14+
var container internal.Container = internal.NewContainer()
6115

62-
var instance interface{}
63-
64-
if concrete, ok := container[abstraction]; ok {
65-
instance = concrete.resolve()
66-
} else {
67-
panic("no concrete found for the abstraction: " + abstraction.String())
68-
}
69-
70-
arguments[i] = reflect.ValueOf(instance)
71-
}
72-
73-
return arguments
74-
}
75-
76-
// Singleton will bind an abstraction to a concrete for further singleton resolves.
77-
// It takes a resolver function which returns the concrete and its return type matches the abstraction (interface).
78-
// The resolver function can have arguments of abstraction that have bound already in Container.
16+
// Singleton creates a singleton for the default instance.
7917
func Singleton(resolver interface{}) {
80-
bind(resolver, true)
18+
container.Singleton(resolver)
8119
}
8220

83-
// Transient will bind an abstraction to a concrete for further transient resolves.
84-
// It takes a resolver function which returns the concrete and its return type matches the abstraction (interface).
85-
// The resolver function can have arguments of abstraction that have bound already in Container.
21+
// Transient creates a transient binding for the default instance.
8622
func Transient(resolver interface{}) {
87-
bind(resolver, false)
23+
container.Transient(resolver)
8824
}
8925

90-
// Reset will reset the container and remove all the bindings.
26+
// Reset removes all bindings in the default instance.
9127
func Reset() {
92-
container = map[reflect.Type]binding{}
28+
container.Reset()
9329
}
9430

95-
// Make will resolve the dependency and return a appropriate concrete of the given abstraction.
96-
// It can take an abstraction (interface reference) and fill it with the related implementation.
97-
// It also can takes a function (receiver) with one or more arguments of the abstractions (interfaces) that need to be
98-
// resolved, Container will invoke the receiver function and pass the related implementations.
31+
// Make binds receiver to the default instance.
9932
func Make(receiver interface{}) {
100-
receiverTypeOf := reflect.TypeOf(receiver)
101-
if receiverTypeOf == nil {
102-
panic("cannot detect type of the receiver, make sure your are passing reference of the object")
103-
}
104-
105-
if receiverTypeOf.Kind() == reflect.Ptr {
106-
abstraction := receiverTypeOf.Elem()
107-
108-
if concrete, ok := container[abstraction]; ok {
109-
instance := concrete.resolve()
110-
reflect.ValueOf(receiver).Elem().Set(reflect.ValueOf(instance))
111-
return
112-
}
113-
114-
panic("no concrete found for the abstraction " + abstraction.String())
115-
}
116-
117-
if receiverTypeOf.Kind() == reflect.Func {
118-
arguments := arguments(receiver)
119-
reflect.ValueOf(receiver).Call(arguments)
120-
return
121-
}
122-
123-
panic("the receiver must be either a reference or a callback")
124-
}
33+
container.Make(receiver)
34+
}

pkg/container/container.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Package container provides an IoC container for Go projects.
2+
// It provides simple, fluent and easy-to-use interface to make dependency injection in GoLang easier.
3+
package container
4+
5+
import (
6+
"reflect"
7+
)
8+
9+
// binding keeps a binding resolver and instance (for singleton bindings).
10+
type binding struct {
11+
resolver interface{} // resolver function
12+
instance interface{} // instance stored for singleton bindings
13+
}
14+
15+
// resolve will return the concrete of related abstraction.
16+
func (b binding) resolve(c Container) interface{} {
17+
if b.instance != nil {
18+
return b.instance
19+
}
20+
21+
return c.invoke(b.resolver)
22+
}
23+
24+
// Container is a map of reflect.Type to binding
25+
type Container map[reflect.Type]binding
26+
27+
// NewContainer returns a new instance of Container
28+
func NewContainer() Container {
29+
return make(Container)
30+
}
31+
32+
// bind will map an abstraction to a concrete and set instance if it's a singleton binding.
33+
func (c Container) bind(resolver interface{}, singleton bool) {
34+
resolverTypeOf := reflect.TypeOf(resolver)
35+
if resolverTypeOf.Kind() != reflect.Func {
36+
panic("the resolver must be a function")
37+
}
38+
39+
for i := 0; i < resolverTypeOf.NumOut(); i++ {
40+
var instance interface{}
41+
if singleton {
42+
instance = c.invoke(resolver)
43+
}
44+
45+
c[resolverTypeOf.Out(i)] = binding{
46+
resolver: resolver,
47+
instance: instance,
48+
}
49+
}
50+
}
51+
52+
// invoke will call the given function and return its returned value.
53+
// It only works for functions that return a single value.
54+
func (c Container) invoke(function interface{}) interface{} {
55+
return reflect.ValueOf(function).Call(c.arguments(function))[0].Interface()
56+
}
57+
58+
// arguments will return resolved arguments of the given function.
59+
func (c Container) arguments(function interface{}) []reflect.Value {
60+
functionTypeOf := reflect.TypeOf(function)
61+
argumentsCount := functionTypeOf.NumIn()
62+
arguments := make([]reflect.Value, argumentsCount)
63+
64+
for i := 0; i < argumentsCount; i++ {
65+
abstraction := functionTypeOf.In(i)
66+
67+
var instance interface{}
68+
69+
if concrete, ok := c[abstraction]; ok {
70+
instance = concrete.resolve(c)
71+
} else {
72+
panic("no concrete found for the abstraction: " + abstraction.String())
73+
}
74+
75+
arguments[i] = reflect.ValueOf(instance)
76+
}
77+
78+
return arguments
79+
}
80+
81+
// Singleton will bind an abstraction to a concrete for further singleton resolves.
82+
// It takes a resolver function which returns the concrete and its return type matches the abstraction (interface).
83+
// The resolver function can have arguments of abstraction that have bound already in Container.
84+
func (c Container) Singleton(resolver interface{}) {
85+
c.bind(resolver, true)
86+
}
87+
88+
// Transient will bind an abstraction to a concrete for further transient resolves.
89+
// It takes a resolver function which returns the concrete and its return type matches the abstraction (interface).
90+
// The resolver function can have arguments of abstraction that have bound already in Container.
91+
func (c Container) Transient(resolver interface{}) {
92+
c.bind(resolver, false)
93+
}
94+
95+
// Reset will reset the container and remove all the bindings.
96+
func (c Container) Reset() {
97+
for k := range c {
98+
delete(c, k)
99+
}
100+
}
101+
102+
// Make will resolve the dependency and return a appropriate concrete of the given abstraction.
103+
// It can take an abstraction (interface reference) and fill it with the related implementation.
104+
// It also can takes a function (receiver) with one or more arguments of the abstractions (interfaces) that need to be
105+
// resolved, Container will invoke the receiver function and pass the related implementations.
106+
func (c Container) Make(receiver interface{}) {
107+
receiverTypeOf := reflect.TypeOf(receiver)
108+
if receiverTypeOf == nil {
109+
panic("cannot detect type of the receiver, make sure your are passing reference of the object")
110+
}
111+
112+
if receiverTypeOf.Kind() == reflect.Ptr {
113+
abstraction := receiverTypeOf.Elem()
114+
115+
if concrete, ok := c[abstraction]; ok {
116+
instance := concrete.resolve(c)
117+
reflect.ValueOf(receiver).Elem().Set(reflect.ValueOf(instance))
118+
return
119+
}
120+
121+
panic("no concrete found for the abstraction " + abstraction.String())
122+
}
123+
124+
if receiverTypeOf.Kind() == reflect.Func {
125+
arguments := c.arguments(receiver)
126+
reflect.ValueOf(receiver).Call(arguments)
127+
return
128+
}
129+
130+
panic("the receiver must be either a reference or a callback")
131+
}

0 commit comments

Comments
 (0)