defval / di

🛠 A full-featured dependency injection container for go programming language.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Allow decorating of services in the container

fionera opened this issue · comments

I started to try to implement this but I dont really understand how the internals are working right now. My Idea was to add another Option to di which is called Decorate.

package main

import (
	"fmt"

	"github.com/goava/di"
)

func main() {
	container, err := di.New(
		di.Provide(NewExample),
	)
	if err != nil {
		panic(err)
	}
	
	container.Invoke(func(e Example) {
		fmt.Println(e) // Will print "Example"
	})

       err = container.Decorate(DecorateExample)
       if err != nil {
		panic(err)
	}

	container.Invoke(func(e Example) {
		fmt.Println(e) // Will print "Example is now Decorated"
	})
}

type Example string

func NewExample() Example {
	return "Example"
}

func DecorateExample(e Example) Example {
	return e + " is now Decorated"
}

This can also be expaned to have options like priority to allow ordered decorations. Of course this only works when the decorated type isnt used before decoration. So you would have to call Decorate before Invoking anything.

@erickskrauch I changed the Name of the issue to make it more clear :D

Closed as unnecessary.

Would you accept a PR if I implement it? This is crucial for a plugin system I currently implement

@fionera For reference type modifying you can use Invoke(). Is this necessary for non-reference types?

No I would only use it for Interfaces but I want the possibility to get the previous instance from the container

func DecorateInterface(iface MyInterface) {
  // some stuff
}

di.Invoke(DecorateIterface)

Is this method not suitable?

Yes thats basically what I mean :) I attached an example on how I would use it. This way I can modify the implementation of a function of an interface without knowing the implementation itself. Symfony calls this System "Decorating" https://symfony.com/doc/current/service_container/service_decoration.html

type Test interface {
	MyFunc() string
}

type t1 struct{}

func NewT1() *t1 {
	return &t1{}
}

func (t *t1) MyFunc() string {
	return "t1"
}

type t2 struct {
	t Test
}

func NewT2(t Test) *t2 {
	return &t2{t}
}


func (t *t2) MyFunc() string {
	return t.t.MyFunc() + " t2"
}

func main() {
	container, err := di.New(
		di.Provide(NewT1, di.As(new(Test))),
		di.Provide(NewT2, di.As(new(Test))),
	)
	if err != nil {
		logrus.Fatal(err)
	}

	err = container.Invoke(func(test Test) {
		logrus.Info(test.MyFunc())
	})
	if err != nil {
		logrus.Fatal(err)
	}
}

Why is Decorate provided as ProvideOption? This looks more like Callback than Decorate.
Actually I'm also wondering why Decorate is needed, it's just like dosth after Resolve ?
Compared with Decorate, I actually hope to have Replace, so that I can replace certain elements in the middle of Invoke => Invoke, such as context.Context.