這次介紹最后一個創建型模式——對象池模式。顧名思義,對象池模式就是預先初始化創建好多個對象,並將之保存在一個池子里。當需要的時候,客戶端就可以從池子里申請一個對象使用,使用完以后再將之放回到池子里。池子里的對象在應用運行期間永遠不會被破壞或回收。
適用場景:
- 當需要的對象的創建成本比較高,且該類型的對象在應用運行期間只需要有限的數量
- 對象是不可變的
- 性能原因:預創建的對象可以顯著提升應用性能
我們在開發中最熟悉的對象池應該是數據庫連接池了。因為網絡因素,數據庫連接池中的每個對象的創建成本都比較高,且應用在運行期間會需要多個數據庫連接對象。另外,每個數據庫的連接池中對象的屬性都是一樣的,且在運行期間這些對象的屬性幾乎通常都是不可變的。
來看個模擬的數據庫連接對象池模型的例子。
iPoolObject.go
type iPoolObject interface {
//This is any id which can be used to compare two different pool objects
getID() string
}
connection.go
type connection struct {
id string
}
func (c *connection) getID() string {
return c.id
}
pool.go
import (
"fmt"
"sync"
)
type pool struct {
idle []iPoolObject
active []iPoolObject
capacity int
muLock *sync.Mutex
}
//initPool Initialize the pool
func initPool(poolObjects []iPoolObject) (*pool, error) {
if len(poolObjects) == 0 {
return nil, fmt.Errorf("cannot craete a pool of 0 length")
}
active := make([]iPoolObject, 0)
pool := &pool{
idle: poolObjects,
active: active,
capacity: len(poolObjects),
muLock: new(sync.Mutex),
}
return pool, nil
}
func (p *pool) loan() (iPoolObject, error) {
p.muLock.Lock()
defer p.muLock.Unlock()
if len(p.idle) == 0 {
return nil, fmt.Errorf("no pool object free. Please request after sometime")
}
obj := p.idle[0]
p.idle = p.idle[1:]
p.active = append(p.active, obj)
fmt.Printf("Loan Pool Object with ID: %s\n", obj.getID())
return obj, nil
}
func (p *pool) receive(target iPoolObject) error {
p.muLock.Lock()
defer p.muLock.Unlock()
err := p.remove(target)
if err != nil {
return err
}
p.idle = append(p.idle, target)
fmt.Printf("Return Pool Object with ID: %s\n", target.getID())
return nil
}
func (p *pool) remove(target iPoolObject) error {
currentActiveLength := len(p.active)
for i, obj := range p.active {
if obj.getID() == target.getID() {
p.active[currentActiveLength-1], p.active[i] = p.active[i], p.active[currentActiveLength-1]
p.active = p.active[:currentActiveLength-1]
return nil
}
}
return fmt.Errorf("targe pool object doesn't belong to the pool")
}
main.go
import (
"log"
"strconv"
)
func main() {
connections := make([]iPoolObject, 0)
for i := 0; i < 3; i++ {
c := &connection{id: strconv.Itoa(i)}
connections = append(connections, c)
}
pool, err := initPool(connections)
if err != nil {
log.Fatalf("Init Pool Error: %s", err)
}
conn1, err := pool.loan()
if err != nil {
log.Fatalf("Pool Loan Error: %s", err)
}
conn2, err := pool.loan()
if err != nil {
log.Fatalf("Pool Loan Error: %s", err)
}
_ = pool.receive(conn1)
_ = pool.receive(conn2)
}
輸出內容為:
Loan Pool Object with ID: 0 Loan Pool Object with ID: 1 Return Pool Object with ID: 0 Return Pool Object with ID: 1
代碼已上傳至GitHub:zhyea / go-patterns / object-pool-pattern
END!
