-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdi.go
149 lines (130 loc) · 4.71 KB
/
di.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright The ActForGood Authors.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://github.com/actforgood/xdi/blob/main/LICENSE
package xdi
import "sync"
const (
// FilterAllRegistered specifies that all registered object IDs should be returned by [Manager.ListIDs].
FilterAllRegistered byte = 1
// FilterInitializedShared specifies that only shared objects that have been already initialized
// (there was at least one [Manager.Get] call for them) should be returned by [Manager.ListIDs].
FilterInitializedShared byte = 2
)
type (
// InitializerFunc is a simple function that returns an instance of a dependency,
// whatever that dependency is (an object / a function / a config, etc.).
InitializerFunc func() interface{}
// Manager is a container for dependencies and their definitions.
// Its APIs are not concurrent safe for use.
Manager struct {
// def contains initialization definitions indexed by a string identifier.
def map[string]Definition
// sharedRegistry contains shared instances.
sharedRegistry map[string]interface{}
}
// Definition holds information needed to initialize a dependency.
Definition struct {
// ID is an identifier of a dependency.
// Example: "app.logger" / "logger" / "config.timeout"
ID string
// Initializer is a factory/function that returns a dependency.
Initializer InitializerFunc
// Shared is a flag whether this dependency is shared or not.
// If it is shared, multiple [Manager.Get] calls for the same identifier will return "same" instance from a registry,
// see also the note below.
// If it is not shared, multiple [Manager.Get] calls for the same identifier will return "different" instances
// (Initializer function will be called on each [Manager.Get] call).
// Note: it makes sense for Initializer function to produce a pointer/reference type if
// you really want it to be shared. On the same principle, if your Initializer function returns
// a singleton for example, shared flag is useless, that instance will always be shared.
Shared bool
}
)
// NewManager instantiates a new dependency injection manager.
func NewManager() *Manager {
return &Manager{
def: make(map[string]Definition),
sharedRegistry: make(map[string]interface{}),
}
}
// AddDefinition adds new definition for a dependency instantiation/retrieval.
// Multiple calls with same definition ID will overwrite previous definition.
func (diMngr *Manager) AddDefinition(def Definition) {
diMngr.def[def.ID] = def
}
// Get returns a dependency if a definition for it was provided previously, or nil otherwise.
func (diMngr *Manager) Get(id string) interface{} {
// look for definition.
def, foundDef := diMngr.def[id]
if !foundDef {
// if it's a shared dependency already instantiated, return it.
if dep, foundRegistry := diMngr.sharedRegistry[id]; foundRegistry {
return dep
}
return nil
}
dep := def.Initializer()
if def.Shared {
// store instance in shared registry.
diMngr.sharedRegistry[id] = dep
// if it's a shared dependency we can dispose of its definition and free memory,
// dependency will be returned from sharedRegistry on an eventual next [Manager.Get] call for it.
delete(diMngr.def, id)
}
return dep
}
// ListIDs returns the IDs list of all registered definitions,
// or of shared and initialized objects if filter is specified.
// Example of usage:
//
// // appShutDown closes all resources at application shutdown.
// func appShutDown(diManager *xdi.Manager) {
// for _, id := range diManager.ListIDs(xdi.FilterInitializedShared) {
// if closable, ok := diManager.Get(id).(io.Closer); ok {
// if err := closable.Close(); err != nil {
// fmt.Printf("could not close resource '%s': %+v\n", id, err)
// }
// }
// }
// }
func (diMngr *Manager) ListIDs(filter ...byte) []string {
var (
fltr = FilterAllRegistered
IDs []string
idx = 0
)
if len(filter) > 0 && filter[0] == FilterInitializedShared {
fltr = FilterInitializedShared
}
switch fltr {
case FilterAllRegistered:
IDs = make([]string, len(diMngr.sharedRegistry)+len(diMngr.def))
for id := range diMngr.def {
IDs[idx] = id
idx++
}
for id := range diMngr.sharedRegistry {
IDs[idx] = id
idx++
}
case FilterInitializedShared:
IDs = make([]string, len(diMngr.sharedRegistry))
for id := range diMngr.sharedRegistry {
IDs[idx] = id
idx++
}
}
return IDs
}
var (
managerSingletonOnce sync.Once
managerSingletonInstance *Manager
)
// ManagerInstance returns a singleon Manager object.
func ManagerInstance() *Manager {
managerSingletonOnce.Do(func() {
managerSingletonInstance = NewManager()
})
return managerSingletonInstance
}