Skip to content

Feature Request: Add by Type, GetByType and GetManyByType #30

Description

@ghstahl

First off, nice library. I was able to add these features and not have to rearchitect anything in the original library. My version is located here

If you accept these features, I can do a proper fork and PR so you can see how I did it.

New Features

The following are non breaking changes to sarulabs/di.

SafeGetByType

// SafeGetByType retrieves the last object added from the Container.
// The object has to belong to this scope or a more generic one.
// If the object does not already exist, it is created and saved in the Container.
// If the object can not be created, it returns an error.
SafeGetByType(rt reflect.Type) (interface{}, error)

GetByType

// GetByType is similar to SafeGetByType but it does not return the error.
// Instead it panics.
GetByType(rt reflect.Type) interface{}

SafeGetManyByType

// SafeGetManyByType retrieves an array of objects from the Container.
// The objects have to belong to this scope or a more generic one.
// If the objects do not already exist, it is created and saved in the Container.
// If the objects can not be created, it returns an error.
SafeGetManyByType(rt reflect.Type) ([]interface{}, error)

GetManyByType

// GetManyByType is similar to SafeGetManyByType but it does not return the error.
// Instead it panics.
GetManyByType(rt reflect.Type) []interface{}

Just like dotnetcore, I would like to register a bunch of services that all implement the same interface. I would like to get back an array of all registered objects, and to do that I need to ask for them by type

For efficiency, type validation is done during registration. After that it should just be lookups.
When you do a GetByType, don't keep calling reflect to get a type of what is a compliled type. do it once and put it in a map.

Registration

type DEF struct

// Def contains information to build and close an object inside a Container.
type Def struct {
	Build            func(ctn Container) (interface{}, error)
	Close            func(obj interface{}) error
	Name             string //[ignored] if Type is used this is overriden and hidden.
	Scope            string
	Tags             []Tag
	Type             reflect.Type //[optional] only if you want to claim that this object also implements these types.
	ImplementedTypes TypeSet
	Unshared         bool
}

Two new fields where added to the Def struct.

	Type             reflect.Type
	ImplementedTypes TypeSet

Type: is the type of the object being registered.
ImplementedTypes: is the types that this object either is or implements

Only the Type option is needed to register and it is automatically added as an implemented type. The ImplementedTypes option is primarily for claiming that the added type supports a given set of interfaces. If this option is added, the original Type is automatically added to the ImplementedTypes set.

The following code will return an error if the added type DOES NOT implemented the ImplementedTypes that were claimed.

type ISomething interface {
	GetName() string
	SetName(name string)
}

type Service struct {
	name            string
}
// SetName ...
func (s *Service) SetName(in string) {
	s.name = in
}

// SetName ...
func (s *Service) GetName() string {
	return s.name
}

func AddTransientService(builder *di.Builder) {
	log.Info().Msg("IoC: AddTransientService")

	types := di.NewTypeSet()
	inter := di.GetInterfaceReflectType((*exampleServices.ISomething)(nil))
	types.Add(inter)
	
	builder.Add(di.Def{
		Name:             "[overidden and hidded if added by Type, can be empty]",
		Scope:            di.App,
		ImplementedTypes: types, //[optional] only if you want to claim that this object also implements these types.
		Type:             reflect.TypeOf(&Service{}),
		Unshared: true,
		Build: func(ctn di.Container) (interface{}, error) {
			service := &Service{
			}
			return service, nil
		},
	})
}

Retrieval

// please put this into a lookup map
inter := di.GetInterfaceReflectType((*exampleServices.ISomething)(nil))

dd := ctn.GetManyByType(inter)
for _, d := range dd {
  ds := d.(exampleServices.ISomething)
  ds.SetName("rabbit")
  log.Info().Msg(ds.GetName())
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions