diff --git a/container/group/example_test.go b/container/group/example_test.go new file mode 100644 index 000000000..dc90f229a --- /dev/null +++ b/container/group/example_test.go @@ -0,0 +1,42 @@ +package group + +import "fmt" + +type Counter struct { + Value int +} + +func (c *Counter) Incr() { + c.Value++ +} + +func ExampleGroup_Get() { + group := NewGroup(func() interface{} { + fmt.Println("Only Once") + return &Counter{} + }) + + // Create a new Counter + group.Get("pass").(*Counter).Incr() + + // Get the created Counter again. + group.Get("pass").(*Counter).Incr() + // Output: + // Only Once +} + +func ExampleGroup_Reset() { + group := NewGroup(func() interface{} { + return &Counter{} + }) + + // Reset the new function and clear all created objects. + group.Reset(func() interface{} { + fmt.Println("reset") + return &Counter{} + }) + + // Create a new Counter + group.Get("pass").(*Counter).Incr() + // Output:reset +} diff --git a/container/group/group.go b/container/group/group.go new file mode 100644 index 000000000..bb9d0568f --- /dev/null +++ b/container/group/group.go @@ -0,0 +1,64 @@ +// Package group provides a sample lazy load container. +// The group only creating a new object not until the object is needed by user. +// And it will cache all the objects to reduce the creation of object. +package group + +import "sync" + +// Group is a lazy load container. +type Group struct { + new func() interface{} + vals map[string]interface{} + sync.RWMutex +} + +// NewGroup news a group container. +func NewGroup(new func() interface{}) *Group { + if new == nil { + panic("container.group: can't assign a nil to the new function") + } + return &Group{ + new: new, + vals: make(map[string]interface{}), + } +} + +// Get gets the object by the given key. +func (g *Group) Get(key string) interface{} { + g.RLock() + v, ok := g.vals[key] + if ok { + g.RUnlock() + return v + } + g.RUnlock() + + // slowpath for group don`t have specified key value + g.Lock() + defer g.Unlock() + v, ok = g.vals[key] + if ok { + return v + } + v = g.new() + g.vals[key] = v + return v +} + +// Reset resets the new function and deletes all existing objects. +func (g *Group) Reset(new func() interface{}) { + if new == nil { + panic("container.group: can't assign a nil to the new function") + } + g.Lock() + g.new = new + g.Unlock() + g.Clear() +} + +// Clear deletes all objects. +func (g *Group) Clear() { + g.Lock() + g.vals = make(map[string]interface{}) + g.Unlock() +} diff --git a/container/group/group_test.go b/container/group/group_test.go new file mode 100644 index 000000000..46a7d4ac2 --- /dev/null +++ b/container/group/group_test.go @@ -0,0 +1,65 @@ +package group + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGroupGet(t *testing.T) { + count := 0 + g := NewGroup(func() interface{} { + count++ + return count + }) + v := g.Get("key_0") + assert.Equal(t, 1, v.(int)) + + v = g.Get("key_1") + assert.Equal(t, 2, v.(int)) + + v = g.Get("key_0") + assert.Equal(t, 1, v.(int)) + assert.Equal(t, 2, count) +} + +func TestGroupReset(t *testing.T) { + g := NewGroup(func() interface{} { + return 1 + }) + g.Get("key") + call := false + g.Reset(func() interface{} { + call = true + return 1 + }) + + length := 0 + for range g.vals { + length++ + } + + assert.Equal(t, 0, length) + + g.Get("key") + assert.Equal(t, true, call) +} + +func TestGroupClear(t *testing.T) { + g := NewGroup(func() interface{} { + return 1 + }) + g.Get("key") + length := 0 + for range g.vals { + length++ + } + assert.Equal(t, 1, length) + + g.Clear() + length = 0 + for range g.vals { + length++ + } + assert.Equal(t, 0, length) +}