Skip to content

Commit df97dca

Browse files
authored
Merge pull request #8 from sensorario/template
add template pattern
2 parents 50f6b3a + b47a5bf commit df97dca

File tree

5 files changed

+186
-0
lines changed

5 files changed

+186
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Template
2+
3+
## Description
4+
5+
Strategy pattern consists in a different way to solve same problem with
6+
different strategies. In Strategy entire algorithm is store in different
7+
classes. In Template pattern, instead, the algorithm is just one. The template
8+
defines steps but some other steps are deferred to user.
9+
10+
## Implementation
11+
12+
In this case the template is a sort of flow of steps to solve a problem. While
13+
in strategy pattern each strategy implements entire solution, in this case the
14+
template define the steps execution but defer one or more steps to the user. In
15+
this case, the deferred step is called `templateSteps`.
16+
17+
```go
18+
type TheTemplate interface {
19+
first() string
20+
second() string
21+
templateSteps(MessageRetriever) string
22+
}
23+
```
24+
25+
The `MessageRetriever` is an interface with `Message()` method. The template
26+
pattern here will joins strings. While first and third steps are implemented,
27+
the custom step is implemented by user.
28+
29+
```go
30+
type MessageRetriever interface {
31+
Message() string
32+
}
33+
```
34+
35+
Now let's implement a concrete `Template`.
36+
37+
```go
38+
type Template struct{}
39+
```
40+
41+
In this case first and second steps are defined. The `templateSteps` will join
42+
different strings receiving the third step from outside.
43+
44+
```go
45+
func (t *Template) first() string {
46+
return "hello"
47+
}
48+
49+
func (t *Template) second() string {
50+
return "template"
51+
}
52+
```
53+
54+
```go
55+
func (t *Template) templateSteps(m MessageRetriever) string {
56+
return strings.Join(
57+
[]string{
58+
t.first(),
59+
m.Message(),
60+
t.second(),
61+
},
62+
" ",
63+
)
64+
}
65+
```

behavioral/template/template.go

Lines changed: 71 additions & 0 deletions
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

Lines changed: 48 additions & 0 deletions
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)