结构型模式

适配器模式

适配器模式用于转换一种接口适配另一种接口。比如,现在有个借口是对json字符串进行分析等,现在有一些yaml文件也要分析,这时候我我们就应该给yaml字符串就个适配器,转换成json字符串,然后就行分析。

代码实现

 1package main
 2
 3import (
 4	"fmt"
 5
 6	"github.com/ghodss/yaml"
 7)
 8
 9type Analysis interface {
10	Analyze(string) error
11}
12
13type JsonAnalysis struct{}
14
15func (*JsonAnalysis) Analyze(jsonStr string) error {
16	// 函数逻辑
17	fmt.Println(jsonStr)
18	return nil
19}
20
21type yamlAnalysis struct {
22	ja *JsonAnalysis
23}
24
25func (y *yamlAnalysis) Analyze(yamlStr string) error {
26	bs, err := yaml.YAMLToJSON([]byte(yamlStr))
27	if err != nil {
28		return err
29	}
30	return y.ja.Analyze(string(bs))
31}
32
33func main() {
34	ja := &JsonAnalysis{}
35	err := ja.Analyze("{\"name\":\"zhy\",\"age\":18}")
36	if err != nil {
37		fmt.Println(err)
38	}
39	ya := &yamlAnalysis{ja: ja}
40	err = ya.Analyze("name: you\nage: 88")
41	if err != nil {
42		fmt.Println(err)
43	}
44}
45
46/*
47{"name":"zhy","age":18}
48{"age":88,"name":"you"}
49*/

桥接模式

桥接模式分离抽象部分和实现部分。使得两部分独立扩展。

桥接模式类似于策略模式,区别在于策略模式封装一系列算法使得算法可以互相替换。

策略模式使抽象部分和实现部分分离,可以独立变化。

比如要发消息,可以发很多种方式,微信、qq、email、短信等等,也可以发很多类型,日常、紧急等的。这时我们可以把发送的的方法做抽象,把类型做实现。

代码实现

 1package main
 2
 3import "fmt"
 4
 5type Message interface {
 6	SendMessage(s string) error
 7}
 8
 9type MessageMethod interface {
10	Send(string) error
11}
12
13type qq struct{}
14
15func (*qq) Send(s string) error {
16	fmt.Println("send qq:", s)
17	return nil
18}
19
20type weixin struct{}
21
22func (*weixin) Send(s string) error {
23	fmt.Println("send weixin:", s)
24	return nil
25}
26
27type email struct{}
28
29func (*email) Send(s string) error {
30	fmt.Println("send email:", s)
31	return nil
32}
33
34type InfoMessage struct {
35	method MessageMethod
36}
37
38func (i *InfoMessage) SendMessage(s string) error {
39	s = "info message: " + s
40	return i.method.Send(s)
41}
42
43type UrgencyMessage struct {
44	method MessageMethod
45}
46
47func (u *UrgencyMessage) SendMessage(s string) error {
48	s = "urgency message: " + s
49	return u.method.Send(s)
50}
51
52func main() {
53	qq := new(qq)
54	weixin := new(weixin)
55	email := new(email)
56
57	info := new(InfoMessage)
58	info.method = qq
59	info.SendMessage("hello")
60
61	info.method = weixin
62	info.SendMessage("hello")
63
64	info.method = email
65	info.SendMessage("hello")
66
67	urgency := new(UrgencyMessage)
68	urgency.method = qq
69	urgency.SendMessage("hello")
70
71	urgency.method = weixin
72	urgency.SendMessage("hello")
73
74	urgency.method = email
75	urgency.SendMessage("hello")
76}
77/*
78send qq: info message: hello
79send weixin: info message: hello
80send email: info message: hello
81send qq: urgency message: hello
82send weixin: urgency message: hello
83send email: urgency message: hello
84*/

装饰器模式

装饰模式使用对象组合的方式动态改变或增加对象行为。Go语言借助于匿名组合和非入侵式接口可以很方便实现装饰模式。使用匿名组合,在装饰器中不必显式定义转调原对象方法。

代码实现

 1package main
 2
 3import "fmt"
 4
 5type Component interface {
 6	Calc() int
 7}
 8
 9type ConcreteComponent struct{}
10
11func (*ConcreteComponent) Calc() int {
12	return 10
13}
14
15type MulDecorator struct {
16	Component
17	num int
18}
19
20func WarpMulDecorator(c Component, num int) Component {
21	return &MulDecorator{
22		Component: c,
23		num:       num,
24	}
25}
26
27func (d *MulDecorator) Calc() int {
28	return d.Component.Calc() * d.num
29}
30
31type AddDecorator struct {
32	Component
33	num int
34}
35
36func WarpAddDecorator(c Component, num int) Component {
37	return &AddDecorator{
38		Component: c,
39		num:       num,
40	}
41}
42
43func (d *AddDecorator) Calc() int {
44	return d.Component.Calc() + d.num
45}
46
47func main() {
48	c := &ConcreteComponent{}
49	md := WarpMulDecorator(c, 2)
50	ad := WarpAddDecorator(c, 3)
51	fmt.Println(md.Calc())
52	fmt.Println(ad.Calc())
53}
54
55/*
5620
5713
58*/

代理模式

代理模式用于延迟处理操作或者在进行实际操作前后进行其它处理。比如在限流中间件中

代码实现

 1package main
 2
 3import (
 4	"errors"
 5	"fmt"
 6	"sync"
 7	"time"
 8)
 9
10type Serve interface {
11	handle(name string) (string, error)
12}
13
14type server struct {
15	lock              sync.RWMutex
16	serve             Serve
17	maxAllowedRequest int
18	rateLimiter       map[string]int
19}
20
21func (s *server) getService(name string) (string, error) {
22	s.lock.RLock()
23	nowNumber := s.rateLimiter[name]
24	s.lock.RUnlock()
25	if nowNumber >= s.maxAllowedRequest {
26		return "", errors.New("rate limit")
27	}
28
29	// 更新计数器
30	s.lock.Lock()
31	s.rateLimiter[name]++
32	s.lock.Unlock()
33
34	str, err := s.serve.handle(name)
35	// 执行后减少计数器
36	s.lock.Lock()
37	s.rateLimiter[name]--
38	s.lock.Unlock()
39	if err != nil {
40		return "", err
41	}
42	return str, nil
43}
44
45type hand struct{}
46
47func (h *hand) handle(name string) (string, error) {
48	time.Sleep(time.Microsecond * 500)
49	return fmt.Sprintf("hello %s", name), nil
50}
51
52func main() {
53	wg := &sync.WaitGroup{}
54	wg.Add(20)
55	s := &server{
56		serve:             &hand{},
57		maxAllowedRequest: 10,
58		rateLimiter:       map[string]int{},
59	}
60	for i := 0; i < 20; i++ {
61		go func(i int) {
62			defer wg.Done()
63			res, err := s.getService("world")
64			if err != nil {
65				fmt.Println(i, "error:", err)
66			} else {
67				fmt.Println(i, "success:", res)
68			}
69		}(i)
70	}
71	wg.Wait()
72}
73
74/*
7515 error: rate limit
7618 error: rate limit
7710 error: rate limit
784 error: rate limit
797 error: rate limit
806 error: rate limit
8116 error: rate limit
829 error: rate limit
8311 error: rate limit
8417 error: rate limit
8514 success: hello world
8619 success: hello world
8713 success: hello world
882 success: hello world
8912 success: hello world
901 success: hello world
913 success: hello world
928 success: hello world
935 success: hello world
940 success: hello world
95*/

组合模式

组合模式统一对象和对象集,使得使用相同接口使用对象和对象集。

组合模式常用于树状结构,用于统一叶子节点和树节点的访问,并且可以用于应用某一操作到所有子节点。

比如要搜索文件夹下的所有文件名

代码实现

 1package main
 2
 3import "fmt"
 4
 5type component interface {
 6	search(string)
 7}
 8
 9type file struct {
10	name string
11}
12
13func (f *file) search(keyword string) {
14	fmt.Printf("Searching for keyword %s in file %s\n", keyword, f.name)
15}
16
17func (f *file) getName() string {
18	return f.name
19}
20
21type folder struct {
22	components []component
23	name       string
24}
25
26func (f *folder) search(keyword string) {
27	fmt.Printf("Serching recursively for keyword %s in folder %s\n", keyword, f.name)
28	for _, composite := range f.components {
29		composite.search(keyword)
30	}
31}
32
33func (f *folder) add(c component) {
34	f.components = append(f.components, c)
35}
36
37func main() {
38	file1 := &file{name: "File1"}
39	file2 := &file{name: "File2"}
40	file3 := &file{name: "File3"}
41
42	folder1 := &folder{
43		name: "Folder1",
44	}
45
46	folder1.add(file1)
47
48	folder2 := &folder{
49		name: "Folder2",
50	}
51	folder2.add(file2)
52	folder2.add(file3)
53	folder2.add(folder1)
54
55	folder2.search("rose")
56}
57/*
58Serching recursively for keyword rose in folder Folder2
59Searching for keyword rose in file File2
60Searching for keyword rose in file File3
61Serching recursively for keyword rose in folder Folder1
62Searching for keyword rose in file File1
63*/

外观模式

API 为facade 模块的外观接口,大部分代码使用此接口简化对facade类的访问。

facade模块同时暴露了a和b 两个Module 的NewXXX和interface,其它代码如果需要使用细节功能时可以直接调用。

代码实现

 1package main
 2
 3import "fmt"
 4
 5func NewAPI() API {
 6	return &apiImpl{
 7		a: NewAModuleAPI(),
 8		b: NewBModuleAPI(),
 9	}
10}
11
12//API is facade interface of facade package
13type API interface {
14	Test() string
15}
16
17//facade implement
18type apiImpl struct {
19	a AModuleAPI
20	b BModuleAPI
21}
22
23func (a *apiImpl) Test() string {
24	aRet := a.a.TestA()
25	bRet := a.b.TestB()
26	return fmt.Sprintf("%s\n%s", aRet, bRet)
27}
28
29//NewAModuleAPI return new AModuleAPI
30func NewAModuleAPI() AModuleAPI {
31	return &aModuleImpl{}
32}
33
34//AModuleAPI ...
35type AModuleAPI interface {
36	TestA() string
37}
38
39type aModuleImpl struct{}
40
41func (*aModuleImpl) TestA() string {
42	return "A module running"
43}
44
45//NewBModuleAPI return new BModuleAPI
46func NewBModuleAPI() BModuleAPI {
47	return &bModuleImpl{}
48}
49
50//BModuleAPI ...
51type BModuleAPI interface {
52	TestB() string
53}
54
55type bModuleImpl struct{}
56
57func (*bModuleImpl) TestB() string {
58	return "B module running"
59}
60
61func main() {
62	api := NewAPI()
63	fmt.Println(api.Test())
64	
65}
66/*
67A module running
68B module running
69*/

享元模式

享元模式从对象中剥离出不发生改变且多个实例需要的重复数据,独立出一个享元,使多个对象共享,从而节省内存以及减少对象数量。

代码实现

 1package main
 2
 3import "fmt"
 4
 5type ImageFlyweightFactory struct {
 6	maps map[string]*ImageFlyweight
 7}
 8
 9var imageFactory *ImageFlyweightFactory
10
11func GetImageFlyweightFactory() *ImageFlyweightFactory {
12	if imageFactory == nil {
13		imageFactory = &ImageFlyweightFactory{
14			maps: make(map[string]*ImageFlyweight),
15		}
16	}
17	return imageFactory
18}
19
20func (f *ImageFlyweightFactory) Get(filename string) *ImageFlyweight {
21	image := f.maps[filename]
22	if image == nil {
23		image = NewImageFlyweight(filename)
24		f.maps[filename] = image
25	}
26
27	return image
28}
29
30type ImageFlyweight struct {
31	data string
32}
33
34func NewImageFlyweight(filename string) *ImageFlyweight {
35	// Load image file
36	data := fmt.Sprintf("image data %s", filename)
37	return &ImageFlyweight{
38		data: data,
39	}
40}
41
42func (i *ImageFlyweight) Data() string {
43	return i.data
44}
45
46type ImageViewer struct {
47	*ImageFlyweight
48}
49
50func NewImageViewer(filename string) *ImageViewer {
51	image := GetImageFlyweightFactory().Get(filename)
52	return &ImageViewer{
53		ImageFlyweight: image,
54	}
55}
56
57func (i *ImageViewer) Display() {
58	fmt.Printf("Display: %s\n", i.Data())
59}

References

https://github.com/senghoo/golang-design-pattern

https://refactoringguru.cn/design-patterns

https://lailin.xyz/post/singleton.html