Skip to content

Commit 40984af

Browse files
committed
add template pattern
1 parent 50f6b3a commit 40984af

File tree

5 files changed

+153
-0
lines changed

5 files changed

+153
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* [Chain of responsiblity](behavioral/chain) [:notebook:](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern)
1010
* [Strategy](behavioral/strategy) [:notebook:](https://en.wikipedia.org/wiki/Strategy_pattern)
1111
* [Command](behavioral/command) [:notebook:](https://en.wikipedia.org/wiki/Command_pattern)
12+
* [Template](behavioral/template) [:notebook:](https://en.wikipedia.org/wiki/Template_pattern)
1213

1314
## [Creational](creational)
1415

behavioral/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ We are going to deal with behaviors instead of define structures or encapsulate
77
* [Strategy](strategy) [:notebook:](https://en.wikipedia.org/wiki/Strategy_pattern)
88
* [Chain of Responsibility](chain) [:notebook:](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern)
99
* [Command](command) [:notebook:](https://en.wikipedia.org/wiki/Command_pattern)
10+
* [Template](template) [:notebook:](https://en.wikipedia.org/wiki/Template_pattern)

behavioral/template/README.md

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Template
2+
3+
## Description
4+
5+
Strategy pattern consists in different way to solve same problem. In Strategy
6+
entire algorithm is store in different classes. In Template pattern the
7+
algorithm is just one. The template define steps but some steps are deferred to
8+
user.
9+
10+
This kind of pattern is widely used in MVC web frameworks when steps are always
11+
the same:
12+
13+
- Handle Request
14+
- Authentication
15+
- Authorization
16+
- Retrieve data from DB
17+
- Do something
18+
- Send Response
19+
20+
All these steps are execute for each http request. Just the `do something`
21+
part is defined by the user.
22+
23+
## Implementation
24+
25+
```go
26+
type TranslatorRing interface {
27+
Next(TranslatorRing)
28+
GetNext() TranslatorRing
29+
KnowsWord(s string) bool
30+
TranslationOf(s string) string
31+
}
32+
```

behavioral/template/template.go

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package template
2+
3+
import "strings"
4+
5+
type MessageRetriever interface {
6+
Message() string
7+
}
8+
9+
type TheTemplate interface {
10+
first() string
11+
second() string
12+
customStep(MessageRetriever) string
13+
}
14+
15+
type Template struct{}
16+
17+
func (t *Template) first() string {
18+
return "hello"
19+
}
20+
21+
func (t *Template) second() string {
22+
return "template"
23+
}
24+
25+
func (t *Template) customStep(m MessageRetriever) string {
26+
return strings.Join(
27+
[]string{
28+
t.first(),
29+
m.Message(),
30+
t.second(),
31+
},
32+
" ",
33+
)
34+
}
35+
36+
type Anonymous struct{}
37+
38+
func (a *Anonymous) first() string {
39+
return "hello"
40+
}
41+
42+
func (a *Anonymous) second() string {
43+
return "template"
44+
}
45+
46+
func (a *Anonymous) customStep(f func() string) string {
47+
return strings.Join(
48+
[]string{
49+
a.first(),
50+
f(),
51+
a.second(),
52+
},
53+
" ",
54+
)
55+
}
56+
57+
type Wrapper struct {
58+
myFunc func() string
59+
}
60+
61+
func (a *Wrapper) Message() string {
62+
if a.myFunc != nil {
63+
return a.myFunc()
64+
}
65+
66+
return ""
67+
}
68+
69+
func MessageRetrieverAdapter(f func() string) MessageRetriever {
70+
return &Wrapper{myFunc: f}
71+
}

behavioral/template/template_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package template
2+
3+
import (
4+
"strings"
5+
"testing"
6+
)
7+
8+
type EmbedTemplate struct {
9+
Template
10+
}
11+
12+
func (m *EmbedTemplate) Message() string {
13+
return "world"
14+
}
15+
16+
func TestCustomStepIsCalled(t *testing.T) {
17+
t.Run("Using interfaces", func(t *testing.T) {
18+
s := &EmbedTemplate{}
19+
res := s.customStep(s)
20+
check(res, " world ", t)
21+
})
22+
23+
t.Run("Define custom step via anonymous function", func(t *testing.T) {
24+
m := new(Anonymous)
25+
res := m.customStep(func() string {
26+
return "world"
27+
})
28+
check(res, " world ", t)
29+
})
30+
31+
t.Run("Using anonymous functions adapted to an interface", func(t *testing.T) {
32+
customStepStep := MessageRetrieverAdapter(func() string {
33+
return "world"
34+
})
35+
if customStepStep == nil {
36+
t.Fatal("Can not continue with a nil custom step")
37+
}
38+
template := Template{}
39+
res := template.customStep(customStepStep)
40+
check(res, " world ", t)
41+
})
42+
}
43+
44+
func check(res string, expected string, t *testing.T) {
45+
if !strings.Contains(res, expected) {
46+
t.Errorf("Expected string '%s' was not found on returned string '%s'\n", expected, res)
47+
}
48+
}

0 commit comments

Comments
 (0)