Package ioc provides inversion of control containers, reflection helpers (GetNamedSetter, GetNamedInstance, GetNamedType) and Factory helpers (Resolve/ResolveNamed).
The ioc.Container and ioc.Values structs implement the Factory interface.
Package ioc is designed with the following goals in mind:
- Well defined behavior (noted in comments and ensured by tests)
- Idiomatic Go usage (within reason)
- Cater for different use cases (e.g. ResolveNamed within Factory functions, MustResolvedNamed for web request scopes)
- Robust runtime behavior (crash safe)
- avoid infinite recursion on resolve
- raise errors on configuration and behavioral errors
- custom ioc.Error struct for diagnosing configuration and behavioral issues, containing the following metadata: calling function, file and line number, called function on package ioc, requested type and name, and an error code representing the type of error.
- Predictable and reasonably efficient performance and memory usage (*to be ensured by benchmark tests)
Package ioc is not dependent on the net/http package.
Please note that package ioc is of production quality, but hasn't been thoroughly tested in production.
Installation
Current Version: 0.1.0
Go Version: 1.2+
go get -u github.com/shelakel/go-ioc
Documentation
See GoDoc on Github
License
This project is under the MIT License. See the LICENSE file for the full license text.
Usage
Please see GoDoc on Github and *_test.go files.
Performance
Due to the use of the reflect package, ioc.Container and ioc.Values are not well suited for temporary storage (e.g. passing state to functions on a hot path).
More benchmarks to be added during optimization.
go test -run=XXX -bench=. -benchmem=true
See reflect_cpu_prof_latest.svg for a CPU profile of the runtime reflection functions.
| Benchmark | Iterations | Avg | Alloc | # Alloc |
|---|---|---|---|---|
| BenchmarkGetNamedSetter_Int | 20000000 | 92.0 ns/op | 33 B/op | 1 allocs/op |
| BenchmarkGetNamedSetter_String | 20000000 | 92.9 ns/op | 33 B/op | 1 allocs/op |
| BenchmarkGetNamedSetter_Interface | 20000000 | 96.5 ns/op | 33 B/op | 1 allocs/op |
| BenchmarkGetNamedSetter_AnonStruct | 20000000 | 114 ns/op | 33 B/op | 1 allocs/op |
| BenchmarkGetNamedSetter_Struct | 20000000 | 93.3 ns/op | 33 B/op | 1 allocs/op |
| BenchmarkGetNamedSetter_DblPtr | 20000000 | 109 ns/op | 33 B/op | 1 allocs/op |
| BenchmarkGetNamedInstance_Int | 20000000 | 102 ns/op | 33 B/op | 1 allocs/op |
| BenchmarkGetNamedInstance_String | 20000000 | 102 ns/op | 33 B/op | 1 allocs/op |
| BenchmarkGetNamedInstance_Interface | 20000000 | 108 ns/op | 33 B/op | 1 allocs/op |
| BenchmarkGetNamedInstance_AnonStruct | 20000000 | 120 ns/op | 33 B/op | 1 allocs/op |
| BenchmarkGetNamedInstance_Struct | 20000000 | 101 ns/op | 33 B/op | 1 allocs/op |
| BenchmarkGetNamedInstance_DblPtr | 20000000 | 120 ns/op | 33 B/op | 1 allocs/op |
| BenchmarkGetNamedType_Int | 100000000 | 20.8 ns/op | 0 B/op | 0 allocs/op |
| BenchmarkGetNamedType_String | 100000000 | 20.9 ns/op | 0 B/op | 0 allocs/op |
| BenchmarkGetNamedType_Interface | 100000000 | 20.8 ns/op | 0 B/op | 0 allocs/op |
| BenchmarkGetNamedType_AnonStruct | 50000000 | 33.4 ns/op | 0 B/op | 0 allocs/op |
| BenchmarkGetNamedType_Struct | 100000000 | 20.9 ns/op | 0 B/op | 0 allocs/op |
| BenchmarkGetNamedType_DblPtr | 50000000 | 33.4 ns/op | 0 B/op | 0 allocs/op |
Preliminary benchmarking on my machine (i7-4770K, 2400MHz RAM) yielded 200 ns per Set/Get operation on ioc.Values, 400 ns per cached/singleton Resolve and 1300 ns per request to resolve via the factory function.
Tests
Tests are written using Ginkgo with the Gomega matchers.
Benchmark functions are written using the testing package.
TODO
- This package isn't actively being worked on *
- Improve performance - for now it is what it is due to the inherent limitations/cost of reflection in Go. This package is well suited for constructing singletons, not so much for use in a 'per request' type scenario.
- Populate function that uses a Factory to populate struct instances via dependency injection on tagged fields. The current thinking is to support dynamic population e.g. standard ioc="constant", dynamic ioc_route="id" via ResolveNamed((*Factory),"route") -> (dynamic Factory).ResolveNamed((type), "id").
- Improve README with Usage examples and topics.
Contributing
Contributions are welcome, especially in the area of additional tests and performance enhancements.
- Find an issue that bugs you / open a new one.
- Discuss.
- Branch off the develop branch, commit, test.
- Submit a pull request / attach the commits to the issue.
