better interface support
This commit is contained in:
parent
24e307bd32
commit
26e6d8261b
69
README.md
69
README.md
@ -15,7 +15,7 @@ import "git.apihub24.de/admin/generic-di"
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// register the Struct Constructor Function for DI
|
// register the Struct Constructor Function for DI
|
||||||
di.Injectable(NewConfiguration)
|
di.Injectable[*Configuration, *Configuration](NewConfiguration)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Configuration struct {
|
type Configuration struct {
|
||||||
@ -40,7 +40,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
di.Injectable(NewGreeter)
|
di.Injectable[*Greeter, *Greeter](NewGreeter)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Greeter struct {
|
type Greeter struct {
|
||||||
@ -67,7 +67,7 @@ package main
|
|||||||
import "git.apihub24.de/admin/generic-di"
|
import "git.apihub24.de/admin/generic-di"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
di.Injectable(NewMessageService)
|
di.Injectable[*MessageService, *MessageService](NewMessageService)
|
||||||
}
|
}
|
||||||
|
|
||||||
type MessageService struct {
|
type MessageService struct {
|
||||||
@ -94,7 +94,7 @@ package main
|
|||||||
import di "git.apihub24.de/admin/generic-di"
|
import di "git.apihub24.de/admin/generic-di"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
msgService := di.Inject[*MessageService]()
|
msgService := di.Inject[*MessageService, *MessageService]()
|
||||||
// prints the message "Hello, Markus"
|
// prints the message "Hello, Markus"
|
||||||
println(msgService.Welcome())
|
println(msgService.Welcome())
|
||||||
}
|
}
|
||||||
@ -110,7 +110,7 @@ package main
|
|||||||
import "git.apihub24.de/admin/generic-di"
|
import "git.apihub24.de/admin/generic-di"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
di.Injectable(newMessageService)
|
di.Injectable[IMessageService, *messageService](newMessageService)
|
||||||
}
|
}
|
||||||
|
|
||||||
type IMessageService interface {
|
type IMessageService interface {
|
||||||
@ -141,13 +141,13 @@ package main
|
|||||||
import di "git.apihub24.de/admin/generic-di"
|
import di "git.apihub24.de/admin/generic-di"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
msgService := di.Inject[IMessageService]()
|
msgService := di.Inject[IMessageService, *messageService]()
|
||||||
// prints the message "Hello, Markus"
|
// prints the message "Hello, Markus"
|
||||||
println(msgService.Welcome())
|
println(msgService.Welcome())
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Replace Instance
|
## Multiple Instance
|
||||||
|
|
||||||
services/message_service.go
|
services/message_service.go
|
||||||
|
|
||||||
@ -157,27 +157,38 @@ package services
|
|||||||
import "git.apihub24.de/admin/generic-di"
|
import "git.apihub24.de/admin/generic-di"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
di.Injectable(newMessageService)
|
di.Injectable[IMessageService, *MessageService](newMessageService)
|
||||||
|
di.Injectable[IMessageService, *MessageServiceMock](newMessageServiceMock)
|
||||||
}
|
}
|
||||||
|
|
||||||
type IMessageService interface {
|
type IMessageService interface {
|
||||||
Welcome() string
|
Welcome() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type messageService struct {
|
type MessageService struct {
|
||||||
greeter *Greeter
|
greeter *Greeter
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMessageService() IMessageService {
|
func newMessageService() IMessageService {
|
||||||
return &messageService{
|
return &messageService{
|
||||||
// here was the Greeter from greeter.go injected
|
// here was the Greeter from greeter.go injected
|
||||||
greeter: di.Inject[*Greeter](),
|
greeter: di.Inject[*Greeter](),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *messageService) Welcome() string {
|
func (ctx *MessageService) Welcome() string {
|
||||||
return ctx.greeter.Greet()
|
return ctx.greeter.Greet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MessageServiceMock struct {}
|
||||||
|
|
||||||
|
func newMessageServiceMock() IMessageService {
|
||||||
|
return &MessageServiceMock{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *MessageServiceMock) Welcome() string {
|
||||||
|
return "Hello, Mock"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
main.go
|
main.go
|
||||||
@ -188,36 +199,14 @@ package main
|
|||||||
import di "git.apihub24.de/admin/generic-di"
|
import di "git.apihub24.de/admin/generic-di"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
msgService := di.Inject[services.IMessageService]()
|
// get the basic implementation
|
||||||
|
msgService := di.Inject[services.IMessageService, *services.MessageService]()
|
||||||
// prints the message "Hello, Markus"
|
// prints the message "Hello, Markus"
|
||||||
println(msgService.Welcome())
|
println(msgService.Welcome())
|
||||||
|
|
||||||
|
// get the mock implementation
|
||||||
|
msgService = di.Inject[services.IMessageService, *services.MessageServiceMock]()
|
||||||
|
// prints the message "Hello, Mock"
|
||||||
|
println(msgService.Welcome())
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
message_service_test.go
|
|
||||||
|
|
||||||
```go
|
|
||||||
package services_test
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// replace the instance of IMessageService with the Mock
|
|
||||||
di.Replace(newMessageServiceMock)
|
|
||||||
}
|
|
||||||
|
|
||||||
type messageServiceMock struct {}
|
|
||||||
|
|
||||||
func newMessageServiceMock() services.IMessageService {
|
|
||||||
return &messageServiceMock{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *messageServiceMock) Welcome() string {
|
|
||||||
return "Hello, Mock"
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMessageServiceMocking(t *testing.T) {
|
|
||||||
service := di.Inject[services.IMessageService]()
|
|
||||||
if service.Welcome() != "Hello, Mock" {
|
|
||||||
t.Errorf("expect Hello, Mock but get %s", service.Welcome())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|||||||
34
injector.go
34
injector.go
@ -13,18 +13,18 @@ var creators = make(map[string]func() any)
|
|||||||
var instances = make(map[string]any)
|
var instances = make(map[string]any)
|
||||||
|
|
||||||
// Injectable marks a constructor Function of a Struct for DI
|
// Injectable marks a constructor Function of a Struct for DI
|
||||||
func Injectable[T any](creator func() T) {
|
func Injectable[T any, K any](creator func() T) {
|
||||||
creatorMutex.Lock()
|
creatorMutex.Lock()
|
||||||
defer creatorMutex.Unlock()
|
defer creatorMutex.Unlock()
|
||||||
creators[getSelector[T]()] = func() any {
|
creators[getSelector[T, K]()] = func() any {
|
||||||
return creator()
|
return creator()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Replace[T any](creator func() T, identifier ...string) {
|
func Replace[T any, K any](creator func() T, identifier ...string) {
|
||||||
Injectable(creator)
|
Injectable[T, K](creator)
|
||||||
selector := getSelector[T]()
|
selector := getSelector[T, K]()
|
||||||
instanceSelector := getSelector[T](identifier...)
|
instanceSelector := getSelector[T, K](identifier...)
|
||||||
cre, creatorExists := creators[selector]
|
cre, creatorExists := creators[selector]
|
||||||
if !creatorExists {
|
if !creatorExists {
|
||||||
return
|
return
|
||||||
@ -38,10 +38,10 @@ func Replace[T any](creator func() T, identifier ...string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Inject gets or create a Instance of the Struct used the Injectable constructor Function
|
// Inject gets or create a Instance of the Struct used the Injectable constructor Function
|
||||||
func Inject[T any](identifier ...string) T {
|
func Inject[T any, K any](identifier ...string) T {
|
||||||
var nilResult T
|
var nilResult T
|
||||||
selector := getSelector[T]()
|
selector := getSelector[T, K]()
|
||||||
instanceSelector := getSelector[T](identifier...)
|
instanceSelector := getSelector[T, K](identifier...)
|
||||||
_, instanceExists := instances[instanceSelector].(T)
|
_, instanceExists := instances[instanceSelector].(T)
|
||||||
if !instanceExists {
|
if !instanceExists {
|
||||||
creator, creatorExists := creators[selector]
|
creator, creatorExists := creators[selector]
|
||||||
@ -62,14 +62,21 @@ func Inject[T any](identifier ...string) T {
|
|||||||
return instances[instanceSelector].(T)
|
return instances[instanceSelector].(T)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Destroy[T any](identifier ...string) {
|
func Destroy[T any, K any](identifier ...string) {
|
||||||
instanceMutex.Lock()
|
instanceMutex.Lock()
|
||||||
defer instanceMutex.Unlock()
|
defer instanceMutex.Unlock()
|
||||||
instanceSelector := getSelector[T](identifier...)
|
instanceSelector := getSelector[T, K](identifier...)
|
||||||
delete(instances, instanceSelector)
|
delete(instances, instanceSelector)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSelector[T any](identifier ...string) string {
|
func getSelector[T any, K any](identifier ...string) string {
|
||||||
|
tName := getTypeName[T]()
|
||||||
|
kName := getTypeName[K]()
|
||||||
|
additionalKey := strings.Join(identifier, "_")
|
||||||
|
return fmt.Sprintf("%s_%s_%s", tName, kName, additionalKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTypeName[T any]() string {
|
||||||
var def T
|
var def T
|
||||||
typeName := ""
|
typeName := ""
|
||||||
typeOf := reflect.TypeOf(def)
|
typeOf := reflect.TypeOf(def)
|
||||||
@ -78,6 +85,5 @@ func getSelector[T any](identifier ...string) string {
|
|||||||
} else {
|
} else {
|
||||||
typeName = reflect.TypeOf((*T)(nil)).Elem().String()
|
typeName = reflect.TypeOf((*T)(nil)).Elem().String()
|
||||||
}
|
}
|
||||||
additionalKey := strings.Join(identifier, "_")
|
return typeName
|
||||||
return fmt.Sprintf("%s_%s", typeName, additionalKey)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,11 +9,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
di.Injectable(newTextService)
|
di.Injectable[*textService, *textService](newTextService)
|
||||||
di.Injectable(newMessageService)
|
di.Injectable[*messageService, *messageService](newMessageService)
|
||||||
di.Injectable(newConfiguration)
|
di.Injectable[*configuration, *configuration](newConfiguration)
|
||||||
di.Injectable(newGreetingService)
|
di.Injectable[greetingService, *textService](newGreetingService)
|
||||||
di.Injectable(newBasicOverridableService)
|
di.Injectable[overridableService, *basicOverridableService](newBasicOverridableService)
|
||||||
|
di.Injectable[overridableService, *basicOverridableServiceMock](newBasicOverridableServiceMock)
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@ -64,7 +65,7 @@ func newConfiguration() *configuration {
|
|||||||
|
|
||||||
func newTextService() *textService {
|
func newTextService() *textService {
|
||||||
return &textService{
|
return &textService{
|
||||||
config: di.Inject[*configuration](),
|
config: di.Inject[*configuration, *configuration](),
|
||||||
id: uuid.NewString(),
|
id: uuid.NewString(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,7 +76,7 @@ func newGreetingService() greetingService {
|
|||||||
|
|
||||||
func newMessageService() *messageService {
|
func newMessageService() *messageService {
|
||||||
return &messageService{
|
return &messageService{
|
||||||
texts: di.Inject[*textService](),
|
texts: di.Inject[*textService, *textService](),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,29 +143,29 @@ func TestInject_Duplicate(t *testing.T) {
|
|||||||
func TestInject_Parallel(t *testing.T) {
|
func TestInject_Parallel(t *testing.T) {
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
println(di.Inject[*textService]().GetID())
|
println(di.Inject[*textService, *textService]().GetID())
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInject_MultipleInstances(t *testing.T) {
|
func TestInject_MultipleInstances(t *testing.T) {
|
||||||
textServiceA := di.Inject[*textService]("a")
|
textServiceA := di.Inject[*textService, *textService]("a")
|
||||||
textServiceB := di.Inject[*textService]("b")
|
textServiceB := di.Inject[*textService, *textService]("b")
|
||||||
if textServiceA.GetID() == textServiceB.GetID() {
|
if textServiceA.GetID() == textServiceB.GetID() {
|
||||||
t.Errorf("expect a seperate instance textServiceA and textServiceB but there was identical")
|
t.Errorf("expect a seperate instance textServiceA and textServiceB but there was identical")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTryInterface(t *testing.T) {
|
func TestTryInterface(t *testing.T) {
|
||||||
greeter := di.Inject[greetingService]()
|
greeter := di.Inject[greetingService, *textService]()
|
||||||
if greeter.Greeting() != "Hello Markus" {
|
if greeter.Greeting() != "Hello Markus" {
|
||||||
t.Errorf("expect greeting Hello Markus")
|
t.Errorf("expect greeting Hello Markus")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTryInterface_MultipleInstances(t *testing.T) {
|
func TestTryInterface_MultipleInstances(t *testing.T) {
|
||||||
greeterA := di.Inject[greetingService]("a")
|
greeterA := di.Inject[greetingService, *textService]("a")
|
||||||
greeterB := di.Inject[greetingService]("b")
|
greeterB := di.Inject[greetingService, *textService]("b")
|
||||||
if greeterA.Greeting() != "Hello Markus" {
|
if greeterA.Greeting() != "Hello Markus" {
|
||||||
t.Errorf("expect greeting Hello Markus")
|
t.Errorf("expect greeting Hello Markus")
|
||||||
}
|
}
|
||||||
@ -177,13 +178,12 @@ func TestTryInterface_MultipleInstances(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOverwriteInjectable(t *testing.T) {
|
func TestOverwriteInjectable(t *testing.T) {
|
||||||
basic := di.Inject[overridableService]()
|
basic := di.Inject[overridableService, *basicOverridableService]()
|
||||||
basicID := basic.GetInstanceID()
|
basicID := basic.GetInstanceID()
|
||||||
if basic.GetValue() != "i am original" {
|
if basic.GetValue() != "i am original" {
|
||||||
t.Errorf("wrong service instance get")
|
t.Errorf("wrong service instance get")
|
||||||
}
|
}
|
||||||
di.Replace(newBasicOverridableServiceMock)
|
basic = di.Inject[overridableService, *basicOverridableServiceMock]()
|
||||||
basic = di.Inject[overridableService]()
|
|
||||||
if basic.GetInstanceID() == basicID {
|
if basic.GetInstanceID() == basicID {
|
||||||
t.Errorf("basic and newOne are the same instance")
|
t.Errorf("basic and newOne are the same instance")
|
||||||
}
|
}
|
||||||
@ -193,6 +193,6 @@ func TestOverwriteInjectable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDestroy(t *testing.T) {
|
func TestDestroy(t *testing.T) {
|
||||||
_ = di.Inject[textService]("a")
|
_ = di.Inject[*textService, *textService]("a")
|
||||||
di.Destroy[textService]("a")
|
di.Destroy[*textService, *textService]("a")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user