Skip to content

Instantly share code, notes, and snippets.

@jacksontong
Last active December 29, 2024 14:58
Show Gist options
  • Save jacksontong/b05f00458416cb830ad6820a9cfc59f4 to your computer and use it in GitHub Desktop.
Save jacksontong/b05f00458416cb830ad6820a9cfc59f4 to your computer and use it in GitHub Desktop.
Simple IOC container implementation in Go
// Package ioc provides a simple inversion of control container for registering and resolving services.
package ioc
import (
"fmt"
"sync"
)
// container holds the registered services and provides thread-safe access.
type container struct {
services map[string]interface{}
mu sync.RWMutex
}
var globalContainer = &container{
services: make(map[string]interface{}),
}
// Register adds a service to the container with the given name.
func Register(name string, service interface{}) {
globalContainer.mu.Lock()
defer globalContainer.mu.Unlock()
globalContainer.services[name] = service
}
// Resolve retrieves a service from the container by name and casts it to the specified type.
func Resolve[T any](name string) (T, error) {
globalContainer.mu.RLock()
defer globalContainer.mu.RUnlock()
service, exists := globalContainer.services[name]
if !exists {
var zero T
return zero, fmt.Errorf("service not found: %s", name)
}
result, ok := service.(T)
if !ok {
var zero T
return zero, fmt.Errorf("service type mismatch: expected %T", zero)
}
return result, nil
}
package ioc
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRegisterAndResolve(t *testing.T) {
type TestService struct {
Name string
}
service := &TestService{Name: "TestService"}
// Register the service
Register("testService", service)
// Resolve the service
resolvedService, err := Resolve[*TestService]("testService")
assert.NoError(t, err)
assert.Equal(t, service, resolvedService)
}
func TestResolveNonExistentService(t *testing.T) {
_, err := Resolve[*struct{}]("nonExistentService")
assert.Error(t, err)
assert.Equal(t, "service not found: nonExistentService", err.Error())
}
func TestResolveServiceTypeMismatch(t *testing.T) {
type TestService struct {
Name string
}
service := &TestService{Name: "TestService"}
// Register the service
Register("testService", service)
// Attempt to resolve the service with a different type
_, err := Resolve[string]("testService")
assert.Error(t, err)
assert.Equal(t, "service type mismatch: expected string", err.Error())
}
package main
import (
"fmt"
"ioc"
)
const myServiceName = "myService"
type MyService struct {
Name string
}
func main() {
// Register a service
ioc.Register(myServiceName, &MyService{Name: "Test Service"})
// Resolve the service
service, err := ioc.Resolve[*MyService](myServiceName)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Service Name:", service.Name)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment