Repository: go-kratos/kratos
Pull request #1452 (2021-09-08 15:08:19)

feat: add container group

codecov-commenter: # [Codecov](https://codecov.io/gh/go-kratos/kratos/pull/1452?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) Report
> Merging [#1452](https://codecov.io/gh/go-kratos/kratos/pull/1452?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) (eaf1586) into [main](https://codecov.io/gh/go-kratos/kratos/commit/378d1ee3c7f4c7131e22af0d1b11c4a3557f7b44?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) (378d1ee) will **increase** coverage by `0.24%`.
> The diff coverage is `81.25%`.

[![Impacted file tree graph](https://codecov.io/gh/go-kratos/kratos/pull/1452/graphs/tree.svg?width=650&height=150&src=pr&token=XwnmBTz78x&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos)](https://codecov.io/gh/go-kratos/kratos/pull/1452?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos)

```diff
@@            Coverage Diff             @@
##             main    #1452      +/-   ##
==========================================
+ Coverage   64.85%   65.10%   +0.24%     
==========================================
  Files          64       65       +1     
  Lines        2928     2960      +32     
==========================================
+ Hits         1899     1927      +28     
- Misses        870      871       +1     
- Partials      159      162       +3     
```


| [Impacted Files](https://codecov.io/gh/go-kratos/kratos/pull/1452?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) | Coverage Δ | |
|---|---|---|
| [container/group/group.go](https://codecov.io/gh/go-kratos/kratos/pull/1452/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos#diff-Y29udGFpbmVyL2dyb3VwL2dyb3VwLmdv) | `81.25% <81.25%> (ø)` | |
| [internal/context/context.go](https://codecov.io/gh/go-kratos/kratos/pull/1452/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos#diff-aW50ZXJuYWwvY29udGV4dC9jb250ZXh0Lmdv) | `100.00% <0.00%> (+2.98%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/go-kratos/kratos/pull/1452?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/go-kratos/kratos/pull/1452?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos). Last update [378d1ee...eaf1586](https://codecov.io/gh/go-kratos/kratos/pull/1452?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos).


Patch:
diff --git a/container/group/group.go b/container/group/group.go
new file mode 100644
index 00000000..bb9d0568
--- /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()
+}


Test patch:
diff --git a/container/group/example_test.go b/container/group/example_test.go
new file mode 100644
index 00000000..dc90f229
--- /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_test.go b/container/group/group_test.go
new file mode 100644
index 00000000..46a7d4ac
--- /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)
+}