-
Notifications
You must be signed in to change notification settings - Fork 2.8k
/
main.go
107 lines (88 loc) · 1.93 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium
// A minimal example implementation of dependency injection in Go using reflection.
// For educational purposes only :-)
package main
import (
"fmt"
"reflect"
)
type A struct{}
func NewA() *A {
return &A{}
}
type B struct {
A *A
}
func NewB(A *A) *B {
return &B{A}
}
func showB(B *B) {
fmt.Printf("B: %#v\n", B)
}
func main() {
c := container{
providers: make(map[int]provider),
byType: make(map[string]int),
objects: make(map[string]reflect.Value),
}
c.provide(NewA)
c.provide(NewB)
c.invoke(showB)
}
type provider struct {
ctor any
ins []reflect.Type
out []reflect.Type
}
type container struct {
nextId int
providers map[int]provider
byType map[string]int
objects map[string]reflect.Value
}
func (c *container) provide(ctor any) {
typ := reflect.TypeOf(ctor)
in := make([]reflect.Type, typ.NumIn())
for i := 0; i < typ.NumIn(); i++ {
in[i] = typ.In(i)
}
out := make([]reflect.Type, typ.NumOut())
for i := 0; i < typ.NumOut(); i++ {
o := typ.Out(i)
out[i] = o
c.byType[o.String()] = c.nextId
}
c.providers[c.nextId] = provider{ctor, in, out}
c.nextId++
}
func (c *container) construct(name string) reflect.Value {
fmt.Printf("constructing %q\n", name)
obj, ok := c.objects[name]
if ok {
return obj
}
id := c.byType[name]
provider := c.providers[id]
ctor := reflect.ValueOf(provider.ctor)
ctorType := ctor.Type()
args := make([]reflect.Value, ctorType.NumIn())
for i := 0; i < ctorType.NumIn(); i++ {
args[i] = c.construct(ctorType.In(i).String())
}
outs := ctor.Call(args)
for i, out := range outs {
t := ctorType.Out(i)
c.objects[t.String()] = out
}
return c.objects[name]
}
func (c *container) invoke(fn any) {
val := reflect.ValueOf(fn)
typ := val.Type()
args := make([]reflect.Value, typ.NumIn())
for i := 0; i < typ.NumIn(); i++ {
args[i] = c.construct(typ.In(i).String())
}
val.Call(args)
}