ioc

package
v0.1.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 30, 2021 License: AGPL-3.0 Imports: 3 Imported by: 0

Documentation

Overview

Package ioc provides simple inversion of control, based on service locator. Its main purpose is to provide singleton services during the initial objects construction.

Overview

Locators register factories to provide values to receptors. A factory is a constructor function for its output type. This function is called to construct a singleton service. It may have arguments that are dependencies, i.e., services to be provided by the locator. A receptor is either a pointer on a service to be located, or a function whose arguments are all services to be located.

By default, services are considered to be singletons and factories are called at most once, when first needed. Methods Fresh and Refresh can be used to force a factory to be called.

Service location is considered to be a singleton service, therefore any Locator provides itself to receptors of type *Locator. This allows receptor functions to call Inject() for some services and Fresh() for some others.

Beware that cyclic dependencies are currently not detected by the package, silently resulting in infinite loops.

Performance

The package uses reflection, hence it is not lightning fast. You should use it when the application starts, to initialise long lasting objects.

Parallelism

Calls to Bind() and Refresh() must all be done from the same goroutine, for instance in init() functions. All other methods can be called from any goroutine.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	NotFactory  = errors.New("Wrong type for a factory")  // Factory's type is not handled.
	NotReceptor = errors.New("Wrong type for a receptor") // Receptor's type is not handled.
	NotFound    = errors.New("No binding")                // No factory for the requested type.
)

Functions

This section is empty.

Types

type Locator

type Locator struct {
	// contains filtered or unexported fields
}

func New

func New() *Locator

New constructs a brand new Locator. The new Locator has bindings only of type *Locator, providing itself for singleton values, and calling Sub() for fresh values.

func (*Locator) Bind

func (self *Locator) Bind(factory interface{}) error

Bind registers a factory for a type. The factory must be a function, and the type of its first returned value is the type it is associated to. If the factory has a second returned value, it must be of type error. The factory cannot have a third returned value.

The factory is called at most once to construct a singleton, when a call to Inject() (or Fresh()) depends on the associated type. The Locator provides singletons for all the arguments of the factory. This often results in other registered factories to be called. Cyclic dependencies are currently not detected and result in infinite loops.

func (*Locator) Fresh

func (self *Locator) Fresh(receptor interface{}) error

Fresh constructs a new value using a registered factory. The receptor must be a pointer the new value is assigned to. The receptor of this method cannot be a function.

Beware that the arguments given to the factory are still singleton values. If you want the factory to use fresh values, make it depend on *Locator and call Fresh() directly from the factory.

func (*Locator) Inject

func (self *Locator) Inject(chain ...interface{}) error

Inject retrieves singletons values from the locator.

Its arguments are receptors that are considered in the order they are provided. Each receptor may be either a pointer or a function. Pointer receptors get their pointed value assigned to the deduced value. For function receptors, each of their arguments is set to a deduced value and the function is executed. If a function receptor returns values, these values are stored to be used as deduced value for subsequent receptors. If the last returned value has type error, this value is not stored and Inject fails with the returned error if it is not nil.

The deduced value is the last stored value assignable to the requested type if there is one, or the singleton value registered for the requested type otherwise.

Example
loc := New()
var v int
loc.Inject(func() int { return 1 }, func() int { return 2 }, func() interface{} { return 3 }, &v)
fmt.Println(v)
Output:

2

func (*Locator) Refresh

func (self *Locator) Refresh(receptor interface{}) error

Refresh contructs a new value and use it as the new singleton value. Same constraints applies as with Fresh. Additionaly, this method cannot be called by multiple goroutines on the same Locator. However, Refresh can be called concurrently on different Locators with the same parent.

func (*Locator) Sub

func (self *Locator) Sub() *Locator

Sub constructs a sub-locator of the current one. The sublocator initially has the same bindings as its parent, except for type *Locator for which the singleton value is the sublocator itself, and fresh values are sublocator of the sublocator. Calls to Bind on the sublocator does not change the bindings on the parent.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL