這次介紹最後一個建立型模式——物件池模式。顧名思義,物件池模式就是預先初始化建立好多個物件,並將之儲存在一個池子裡。當需要的時候,客戶端就可以從池子裡申請一個物件使用,使用完以後再將之放回到池子裡。池子裡的物件在應用執行期間永遠不會被破壞或回收。
適用場景:
- 當需要的物件的建立成本比較高,且該型別的物件在應用執行期間只需要有限的數量
- 物件是不可變的
- 效能原因:預建立的物件可以顯著提升應用效能
我們在開發中最熟悉的物件池應該是資料庫連線池了。因為網路因素,資料庫連線池中的每個物件的建立成本都比較高,且應用在執行期間會需要多個資料庫連線物件。另外,每個資料庫的連線池中物件的屬性都是一樣的,且在執行期間這些物件的屬性幾乎通常都是不可變的。
來看個模擬的資料庫連線物件池模型的例子。
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!