diff --git a/README.md b/README.md index 9cc7d12..361e08d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ import "git.apihub24.de/admin/generic-di" func init() { // register the Struct Constructor Function for DI - di.Injectable(NewConfiguration) + di.Injectable[*Configuration, *Configuration](NewConfiguration) } type Configuration struct { @@ -40,7 +40,7 @@ import ( ) func init() { - di.Injectable(NewGreeter) + di.Injectable[*Greeter, *Greeter](NewGreeter) } type Greeter struct { @@ -67,7 +67,7 @@ package main import "git.apihub24.de/admin/generic-di" func init() { - di.Injectable(NewMessageService) + di.Injectable[*MessageService, *MessageService](NewMessageService) } type MessageService struct { @@ -94,7 +94,7 @@ package main import di "git.apihub24.de/admin/generic-di" func main() { - msgService := di.Inject[*MessageService]() + msgService := di.Inject[*MessageService, *MessageService]() // prints the message "Hello, Markus" println(msgService.Welcome()) } @@ -110,7 +110,7 @@ package main import "git.apihub24.de/admin/generic-di" func init() { - di.Injectable(newMessageService) + di.Injectable[IMessageService, *messageService](newMessageService) } type IMessageService interface { @@ -141,13 +141,13 @@ package main import di "git.apihub24.de/admin/generic-di" func main() { - msgService := di.Inject[IMessageService]() + msgService := di.Inject[IMessageService, *messageService]() // prints the message "Hello, Markus" println(msgService.Welcome()) } ``` -## Replace Instance +## Multiple Instance services/message_service.go @@ -157,27 +157,38 @@ package services import "git.apihub24.de/admin/generic-di" func init() { - di.Injectable(newMessageService) + di.Injectable[IMessageService, *MessageService](newMessageService) + di.Injectable[IMessageService, *MessageServiceMock](newMessageServiceMock) } type IMessageService interface { Welcome() string } -type messageService struct { +type MessageService struct { greeter *Greeter } -func NewMessageService() IMessageService { +func newMessageService() IMessageService { return &messageService{ // here was the Greeter from greeter.go injected greeter: di.Inject[*Greeter](), } } -func (ctx *messageService) Welcome() string { +func (ctx *MessageService) Welcome() string { return ctx.greeter.Greet() } + +type MessageServiceMock struct {} + +func newMessageServiceMock() IMessageService { + return &MessageServiceMock{} +} + +func (svc *MessageServiceMock) Welcome() string { + return "Hello, Mock" +} ``` main.go @@ -188,36 +199,14 @@ package main import di "git.apihub24.de/admin/generic-di" func main() { - msgService := di.Inject[services.IMessageService]() + // get the basic implementation + msgService := di.Inject[services.IMessageService, *services.MessageService]() // prints the message "Hello, Markus" 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()) - } -} -``` diff --git a/injector.go b/injector.go index feaaabb..b1dc3f7 100644 --- a/injector.go +++ b/injector.go @@ -13,18 +13,18 @@ var creators = make(map[string]func() any) var instances = make(map[string]any) // 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() defer creatorMutex.Unlock() - creators[getSelector[T]()] = func() any { + creators[getSelector[T, K]()] = func() any { return creator() } } -func Replace[T any](creator func() T, identifier ...string) { - Injectable(creator) - selector := getSelector[T]() - instanceSelector := getSelector[T](identifier...) +func Replace[T any, K any](creator func() T, identifier ...string) { + Injectable[T, K](creator) + selector := getSelector[T, K]() + instanceSelector := getSelector[T, K](identifier...) cre, creatorExists := creators[selector] if !creatorExists { 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 -func Inject[T any](identifier ...string) T { +func Inject[T any, K any](identifier ...string) T { var nilResult T - selector := getSelector[T]() - instanceSelector := getSelector[T](identifier...) + selector := getSelector[T, K]() + instanceSelector := getSelector[T, K](identifier...) _, instanceExists := instances[instanceSelector].(T) if !instanceExists { creator, creatorExists := creators[selector] @@ -62,14 +62,21 @@ func Inject[T any](identifier ...string) T { return instances[instanceSelector].(T) } -func Destroy[T any](identifier ...string) { +func Destroy[T any, K any](identifier ...string) { instanceMutex.Lock() defer instanceMutex.Unlock() - instanceSelector := getSelector[T](identifier...) + instanceSelector := getSelector[T, K](identifier...) 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 typeName := "" typeOf := reflect.TypeOf(def) @@ -78,6 +85,5 @@ func getSelector[T any](identifier ...string) string { } else { typeName = reflect.TypeOf((*T)(nil)).Elem().String() } - additionalKey := strings.Join(identifier, "_") - return fmt.Sprintf("%s_%s", typeName, additionalKey) + return typeName } diff --git a/injector_test.go b/injector_test.go index dc8710c..9f8afea 100644 --- a/injector_test.go +++ b/injector_test.go @@ -9,11 +9,12 @@ import ( ) func init() { - di.Injectable(newTextService) - di.Injectable(newMessageService) - di.Injectable(newConfiguration) - di.Injectable(newGreetingService) - di.Injectable(newBasicOverridableService) + di.Injectable[*textService, *textService](newTextService) + di.Injectable[*messageService, *messageService](newMessageService) + di.Injectable[*configuration, *configuration](newConfiguration) + di.Injectable[greetingService, *textService](newGreetingService) + di.Injectable[overridableService, *basicOverridableService](newBasicOverridableService) + di.Injectable[overridableService, *basicOverridableServiceMock](newBasicOverridableServiceMock) } type ( @@ -64,7 +65,7 @@ func newConfiguration() *configuration { func newTextService() *textService { return &textService{ - config: di.Inject[*configuration](), + config: di.Inject[*configuration, *configuration](), id: uuid.NewString(), } } @@ -75,7 +76,7 @@ func newGreetingService() greetingService { func newMessageService() *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) { for i := 0; i < 20; i++ { go func() { - println(di.Inject[*textService]().GetID()) + println(di.Inject[*textService, *textService]().GetID()) }() } } func TestInject_MultipleInstances(t *testing.T) { - textServiceA := di.Inject[*textService]("a") - textServiceB := di.Inject[*textService]("b") + textServiceA := di.Inject[*textService, *textService]("a") + textServiceB := di.Inject[*textService, *textService]("b") if textServiceA.GetID() == textServiceB.GetID() { t.Errorf("expect a seperate instance textServiceA and textServiceB but there was identical") } } func TestTryInterface(t *testing.T) { - greeter := di.Inject[greetingService]() + greeter := di.Inject[greetingService, *textService]() if greeter.Greeting() != "Hello Markus" { t.Errorf("expect greeting Hello Markus") } } func TestTryInterface_MultipleInstances(t *testing.T) { - greeterA := di.Inject[greetingService]("a") - greeterB := di.Inject[greetingService]("b") + greeterA := di.Inject[greetingService, *textService]("a") + greeterB := di.Inject[greetingService, *textService]("b") if greeterA.Greeting() != "Hello Markus" { t.Errorf("expect greeting Hello Markus") } @@ -177,13 +178,12 @@ func TestTryInterface_MultipleInstances(t *testing.T) { } func TestOverwriteInjectable(t *testing.T) { - basic := di.Inject[overridableService]() + basic := di.Inject[overridableService, *basicOverridableService]() basicID := basic.GetInstanceID() if basic.GetValue() != "i am original" { t.Errorf("wrong service instance get") } - di.Replace(newBasicOverridableServiceMock) - basic = di.Inject[overridableService]() + basic = di.Inject[overridableService, *basicOverridableServiceMock]() if basic.GetInstanceID() == basicID { t.Errorf("basic and newOne are the same instance") } @@ -193,6 +193,6 @@ func TestOverwriteInjectable(t *testing.T) { } func TestDestroy(t *testing.T) { - _ = di.Inject[textService]("a") - di.Destroy[textService]("a") + _ = di.Inject[*textService, *textService]("a") + di.Destroy[*textService, *textService]("a") }