samber/do的使用及其原理

使用对象容器可更好的帮助程序管理单例的对象。将初始化好的对象放入对象容器中,使用时向容器索取而非使用new的方式主动创建,这是一种控制反转(IOC)的思想,或者说它是依赖注入(DI)。
go语言github.com/samber/do库可以实现简单的依赖注入,且该库没有使用反射,不会有太大的性能消耗。
如下是samber/do库的一个简单的demo

package dependency_injection

import (
	"fmt"
	"github.com/samber/do"
	"testing"
)

type A struct {
	B
	C
}

/*
      A
    /   \
   B     C
          \
           D
*/

type B struct {
	Name string
}

type C struct {
	Name string
	D
}

type D struct {
	Name string
}

func NewA(i *do.Injector) (A, error) {
	return A{
		B: do.MustInvoke[B](i),
		C: do.MustInvoke[C](i),
	}, nil
}

func NewB(i *do.Injector) (B, error) {
	return B{Name: "It is B"}, nil
}

func NewC(i *do.Injector) (C, error) {
	return C{
		Name: "It is C",
		D:    do.MustInvoke[D](i),
	}, nil
}

func NewD(i *do.Injector) (D, error) {
	return D{Name: "It is D"}, nil
}

func TestInject(t *testing.T) {
	injector := do.New()
	do.Provide(injector, NewA)
	do.Provide(injector, NewB)
	do.Provide(injector, NewC)
	do.Provide(injector, NewD)

	fmt.Println(do.MustInvoke[A](injector))
	fmt.Println(do.MustInvoke[B](injector))
	fmt.Println(do.MustInvoke[C](injector))
	fmt.Println(do.MustInvoke[D](injector))
}

该库的原理如下,可以说是一种懒加载的方式。
举个生动的例子,警察抓住了一个小偷A,这个小偷A是团伙作案,审讯的时候小偷A供出了小偷B,小偷B被捕,审讯的时候小偷B供出了小偷C,小偷D…就这样,一整个团伙都被供了出来;也就是一个对象依赖关系被树状或网状的初始化出来。

package dependency_injection

import (
	"fmt"
	"testing"
)

type a struct {
	b
	c
}

type b struct {
	name string
}

type c struct {
	name string
	d
}

type d struct {
	name string
}

func newA() (a, error) {
	return a{
		b: Get[b](),
		c: Get[c](),
	}, nil
}

func newB() (b, error) {
	return b{
		name: "b",
	}, nil
}

func newC() (c, error) {
	return c{
		name: "c",
		d:    Get[d](),
	}, nil
}

func newD() (d, error) {
	return d{
		name: "d",
	}, nil
}

type Container[V any] map[string]V

type Callback[T any] func() (T, error)

var container = make(Container[any])

func Set[T any](c Callback[T]) {
	container[getName[T]()] = c
}

func getName[T any]() string {
	if res := fmt.Sprintf("%T", *new(T)); res != "<nil>" {
		return res
	}
	return fmt.Sprintf("%T", new(T))
}

func Get[T any]() T {
	callBack, ok := container[getName[T]()]
	if !ok {
		panic("not found")
	}
	t2, err := callBack.(Callback[T])()
	if err != nil {
		panic(err)
	}
	return t2
}

func TestContainer(t *testing.T) {
	Set(newC)
	Set(newD)
	Set(newA)
	Set(newB)

	fmt.Println(Get[a]())
	fmt.Println(Get[b]())
	fmt.Println(Get[c]())
	fmt.Println(Get[d]())
}

func Test(t *testing.T) {
	type s struct {
		a string
		b string
		c
	}

	ss := s{
		a: func() string {
			fmt.Println("init a")
			return "a"
		}(),

		b: func() string {
			fmt.Println("init b")
			return "b"
		}(),
		c: func() c {
			fmt.Println("init c")
			return c{name: "is c", d: func() d {
				fmt.Println("init d")
				return d{
					name: "d",
				}
			}()}
		}(),
	}
	fmt.Println(ss)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

metabit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值