【设计模式】11、flyweight 享元模式

发布于:2024-04-27 ⋅ 阅读:(28) ⋅ 点赞:(0)

十一、flyweight

https://refactoringguru.cn/design-patterns/flyweight

大量重复的对象, 如果很消耗资源, 没必要每次都初始化, 可以共用, 共享. 这就是 flyweight 享元模式.

各种池技术: 线程池, 数据库连接池, http 网络连接池, 都是应用场景

参考: 共享单车示例
https://www.bilibili.com/video/BV1Ka4y1L7jg/?spm_id_from=333.337.search-card.all.click&vd_source=5dfe92471f5072eaffbf480e25acd82d

11.1 pool 连接池

11flyweight/111pool
├── conn.go
├── pool.go
├── pool_test.go
└── readme.md

11.1.1 pool_test.go

package _11pool

import (
    "github.com/stretchr/testify/require"
    "testing"
)

/*
=== RUN   TestPoolWithSize1
[连接池] 申请连接资源, 开始
[连接池] 申请连接资源, 成功
数据库连接池: 连接中...
[连接池] 申请连接资源, 开始
[连接池] 归还连接资源, 成功
[连接池] 申请连接资源, 开始
[连接池] 申请连接资源, 成功
数据库连接池: 连接中...
--- PASS: TestPoolWithSize1 (0.00s)
PASS
*/
func TestPoolWithSize1(t *testing.T) {
    conns := []conn{&dbConn{}}
    p := NewPool(conns)

    c1, err := p.getConn()
    require.NoError(t, err)
    require.NotNil(t, c1)
    c1.connect()

    c2, err := p.getConn()
    require.Error(t, err)
    require.Nil(t, c2)

    p.putConn(c1)

    c3, err := p.getConn()
    require.NoError(t, err)
    require.NotNil(t, c3)
    c3.connect()
}

/*
=== RUN   TestPoolWithSize5
[连接池] 申请连接资源, 开始
[连接池] 申请连接资源, 成功
数据库连接池: 连接中...
[连接池] 归还连接资源, 成功
[连接池] 申请连接资源, 开始
[连接池] 申请连接资源, 成功
数据库连接池: 连接中...
[连接池] 归还连接资源, 成功
[连接池] 申请连接资源, 开始
[连接池] 申请连接资源, 成功
数据库连接池: 连接中...
[连接池] 归还连接资源, 成功
[连接池] 申请连接资源, 开始
[连接池] 申请连接资源, 成功
数据库连接池: 连接中...
[连接池] 归还连接资源, 成功
[连接池] 申请连接资源, 开始
[连接池] 申请连接资源, 成功
数据库连接池: 连接中...
[连接池] 归还连接资源, 成功
--- PASS: TestPoolWithSize5 (0.00s)
PASS
*/
func TestPoolWithSize5(t *testing.T) {
    conns := []conn{&dbConn{}, &dbConn{}, &dbConn{}, &httpConn{}, &wsConn{}}
    p := NewPool(conns)
    for i := 0; i < len(conns); i++ {
        c, err := p.getConn()
        require.NoError(t, err)
        require.NotNil(t, c)

        c.connect()

        p.putConn(c)
    }
}

11.1.2 pool.go

package _11pool

import (
    "fmt"
)

// IPool 连接池
type IPool interface {
    getConn() (conn, error)
    putConn(conn)
}

// 连接池实现
type pool struct {
    connDict map[conn]struct{}
}

func NewPool(conns []conn) IPool {
    connDict := map[conn]struct{}{}
    for _, c := range conns {
        connDict[c] = struct{}{}
    }
    return &pool{
        connDict: connDict,
    }
}

// 获取连接
func (p *pool) getConn() (conn, error) {
    fmt.Println("[连接池] 申请连接资源, 开始")
    l := len(p.connDict)
    if l == 0 {
        return nil, fmt.Errorf("[连接池] 申请连接资源, 失败: 已无空闲连接资源")
    }
    fmt.Println("[连接池] 申请连接资源, 成功")

    var c conn
    for cIter := range p.connDict {
        c = cIter
        break
    }
    delete(p.connDict, c)
    return c, nil
}

// 归还连接
func (p *pool) putConn(c conn) {
    if c == nil {
        return
    }
    p.connDict[c] = struct{}{}
    fmt.Println("[连接池] 归还连接资源, 成功")
}

11.1.3 conn.go

package _11pool

import "fmt"

// 连接
type conn interface {
    connect()
}

type dbConn struct{}

func (c *dbConn) connect() {
    fmt.Println("数据库连接池: 连接中...")
}

type httpConn struct{}

func (c *httpConn) connect() {
    fmt.Println("http连接池: 连接中...")
}

type wsConn struct{}

func (c *wsConn) connect() {
    fmt.Println("http连接池: 连接中...")
}

11.2 chess_board

象棋棋盘

11flyweight/112chess_board
├── chess.go
├── chess_test.go
└── readme.md

11.2.1 chess_test.go

package _12chess_board

import (
	"github.com/stretchr/testify/require"
	"testing"
)

/*
=== RUN   TestChess
--- PASS: TestChess (0.00s)
PASS
*/
func TestChess(t *testing.T) {
	board1 := NewChessBoard()
	// board1.Move(1, 1, 2)

	board2 := NewChessBoard()
	// board2.Move(2, 2, 3)

	require.EqualValues(t, board1.chessPieces[1].Unit, board2.chessPieces[1].Unit)
	require.EqualValues(t, board1.chessPieces[2].Unit, board2.chessPieces[2].Unit)
}

11.2.2 chess.go

package _12chess_board

var ChessPieceUnits = map[int]*ChessPieceUnit{
	1: {ID: 1, Name: "车", Color: "red"},
	2: {ID: 2, Name: "马", Color: "yellow"},
	3: {ID: 3, Name: "炮", Color: "blue"},
}

// ChessPiece 棋子
type ChessPiece struct {
	Unit *ChessPieceUnit
	X    int
	Y    int
}

// ChessPieceUnit 棋子享元
type ChessPieceUnit struct {
	ID    int
	Name  string
	Color string
}

// NewChessPieceUnit 创建棋子
func NewChessPieceUnit(id int) *ChessPieceUnit {
	return ChessPieceUnits[id]
}

// ChessBoard 棋盘
type ChessBoard struct {
	chessPieces map[int]*ChessPiece
}

func NewChessBoard() *ChessBoard {
	board := &ChessBoard{chessPieces: map[int]*ChessPiece{}}
	for id := range ChessPieceUnits {
		board.chessPieces[id] = &ChessPiece{
			Unit: NewChessPieceUnit(id),
			X:    0,
			Y:    0,
		}
	}
	return board
}

// Move 移动棋子
func (c *ChessBoard) Move(id, x, y int) {
	c.chessPieces[id].X = x
	c.chessPieces[id].Y = y
}