缘起

最近复习设计模式
拜读谭勇德的<<设计模式就该这样学>>
本系列笔记拟采用golang练习之

享元模式

享元模式(Flyweight Pattern)又叫作轻量级模式,是对象池的一种实现。享元模式提供了减少对象数量从而改善应用所需的对象结构的方式。其宗旨是共享细粒度对象,不必为每个访问者都创建一个单独的对象,以此来降低内存的消耗,属于结构型设计模式。
_

场景

  • 某火车票查询系统, 可根据发站和到站, 查询余票信息
  • 火车票包含基本信息(发站, 到站, 经停站, 出发时间, 到站时间…)和剩余票数信息
  • 基本信息字段较多, 且只跟发站和到站相关, 因此可采用享元模式进行池化处理
  • 剩余票数信息由于实时变化, 因此由余票服务另外提供

设计

  • ITicket: 定义车票基本信息接口
  • ITicketRemaining: 继承ITicket, 并添加余票数信息
  • ITicketService: 定义车票信息服务接口
  • ITicketRemainingService: 定义余票信息服务接口. 根据发站和到站, 查询余票信息.
  • tMockTicket: 车票信息实体, 实现ITicket接口
  • tMockTicketService: 车票信息服务, 通过享元模式池化了车票信息.
  • tMockTicketRemaining: 余票信息实体, 实现ITicketRemaining接口
  • tMockTicketRemainingService: 余票信息服务, 通过ITicketService获取车票基本信息. 根据发站和到站, 查询余票信息.

单元测试

flyweight_pattern_test.go

  1. package structural_patterns
  2. import (
  3. "learning/gooop/structural_patterns/flyweight"
  4. "testing"
  5. )
  6. func Test_FlyweightPattern(t *testing.T) {
  7. from := "福田"
  8. to := "广州南"
  9. ticket := flyweight.NewMockTicket(1, from, to, 100)
  10. flyweight.MockTicketService.Save(ticket)
  11. flyweight.MockTicketRemainingService.Save(ticket.ID(), 10)
  12. remaining := flyweight.MockTicketRemainingService.Get(from, to)
  13. t.Logf("from=%s, to=%s, price=%v, remaining=%v\n", remaining.From(), remaining.To(), remaining.Price(), remaining.Remaining())
  14. }

测试输出

  1. t$ go test -v flyweight_pattern_test.go
  2. === RUN Test_FlyweightPattern
  3. flyweight_pattern_test.go:16: from=福田, to=广州南, price=100, remaining=10
  4. --- PASS: Test_FlyweightPattern (0.00s)
  5. PASS
  6. ok command-line-arguments 0.003s

ITicket.go

定义车票基本信息接口

  1. package flyweight
  2. // 车票信息
  3. type ITicket interface {
  4. ID() int
  5. From() string
  6. To() string
  7. LeavingTime() string
  8. ArrivalTime() string
  9. InterList() []string
  10. Price() float64
  11. }

ITicketRemaining.go

继承ITicket, 并添加余票数信息

  1. package flyweight
  2. // 余票信息
  3. type ITicketRemaining interface {
  4. ITicket
  5. Remaining() int
  6. }

ITicketService.go

定义车票信息服务接口

  1. package flyweight
  2. type ITicketService interface {
  3. Get(from string, to string) ITicket
  4. Save(it ITicket)
  5. }

ITicketRemainingService.go

定义余票信息服务接口, 根据发站和到站, 查询余票信息.

  1. package flyweight
  2. type ITicketRemainingService interface {
  3. Get(from string, to string) ITicketRemaining
  4. Save(id int, num int)
  5. }

**

tMockTicket.go

车票信息实体, 实现ITicket接口

  1. package flyweight
  2. import "strings"
  3. type tMockTicket struct {
  4. iID int
  5. sFrom string
  6. sTo string
  7. sLeavingTime string
  8. sArrivalTime string
  9. mInterList []string
  10. fPrice float64
  11. iRemaining int
  12. }
  13. func NewMockTicket(id int, from string, to string, price float64) *tMockTicket {
  14. return &tMockTicket{
  15. iID: id,
  16. sFrom: from,
  17. sTo: to,
  18. sLeavingTime: "09:00",
  19. sArrivalTime: "11:30",
  20. mInterList: strings.Split("深圳北,虎门", ","),
  21. fPrice: price,
  22. }
  23. }
  24. func (me *tMockTicket) ID() int {
  25. return me.iID
  26. }
  27. func (me *tMockTicket) From() string {
  28. return me.sFrom
  29. }
  30. func (me *tMockTicket) To() string {
  31. return me.sTo
  32. }
  33. func (me *tMockTicket) LeavingTime() string {
  34. return me.sLeavingTime
  35. }
  36. func (me *tMockTicket) ArrivalTime() string {
  37. return me.sArrivalTime
  38. }
  39. func (me *tMockTicket) InterList() []string {
  40. return me.mInterList
  41. }
  42. func (me *tMockTicket) Price() float64 {
  43. return me.fPrice
  44. }

tMockTicketService.go

车票信息服务, 实现ITicketService接口, 通过享元模式池化了车票信息.

  1. package flyweight
  2. import "sync"
  3. type tMockTicketService struct {
  4. mTickets map[string]ITicket
  5. mRWMutex *sync.RWMutex
  6. }
  7. func newMockTicketService() *tMockTicketService {
  8. return &tMockTicketService{
  9. make(map[string]ITicket, 0),
  10. new(sync.RWMutex),
  11. }
  12. }
  13. func (me *tMockTicketService) Get(from string, to string) ITicket {
  14. k := from + "-" + to
  15. me.mRWMutex.RLock()
  16. defer me.mRWMutex.RUnlock()
  17. it,ok := me.mTickets[k]
  18. if ok {
  19. return it
  20. } else {
  21. return nil
  22. }
  23. }
  24. func (me *tMockTicketService) Save(it ITicket) {
  25. k := it.From() + "-" + it.To()
  26. me.mRWMutex.Lock()
  27. defer me.mRWMutex.Unlock()
  28. me.mTickets[k] = it
  29. }
  30. var MockTicketService ITicketService = newMockTicketService()

tMockTicketRemaining.go

余票信息实体, 实现ITicketRemaining接口

  1. package flyweight
  2. type tMockTicketRemaining struct {
  3. ITicket
  4. iRemaining int
  5. }
  6. func newMockTicketRemaining(it ITicket, num int) *tMockTicketRemaining {
  7. return &tMockTicketRemaining{
  8. it, num,
  9. }
  10. }
  11. func (me *tMockTicketRemaining) Remaining() int {
  12. return me.iRemaining
  13. }

tMockTicketRemainingService.go

余票信息服务, 实现ITicketRemainingService接口. 通过ITicketService获取车票基本信息. 根据发站和到站, 查询余票信息.

  1. package flyweight
  2. import "sync"
  3. type tMockTicketRemainingService struct {
  4. mRemaining map[int]int
  5. mRWMutex *sync.RWMutex
  6. }
  7. func newMockTicketRemainingService() *tMockTicketRemainingService {
  8. return &tMockTicketRemainingService{
  9. make(map[int]int, 16),
  10. new(sync.RWMutex),
  11. }
  12. }
  13. func (me *tMockTicketRemainingService) Get(from string, to string) ITicketRemaining {
  14. ticket := MockTicketService.Get(from, to)
  15. if ticket == nil {
  16. return nil
  17. }
  18. r := newMockTicketRemaining(ticket, 0)
  19. me.mRWMutex.RLock()
  20. defer me.mRWMutex.RUnlock()
  21. num,ok := me.mRemaining[ticket.ID()]
  22. if ok {
  23. r.iRemaining = num
  24. }
  25. return r
  26. }
  27. func (me *tMockTicketRemainingService) Save(id int, num int) {
  28. me.mRWMutex.Lock()
  29. defer me.mRWMutex.Unlock()
  30. me.mRemaining[id] = num
  31. }
  32. var MockTicketRemainingService ITicketRemainingService = newMockTicketRemainingService()

享元模式小结

享元模式是对象池的一种应用.
享元模式的优点
(1)减少对象的创建,降低内存中对象的数量,降低系统的内存,提高效率。
(2)减少内存之外的其他资源占用。
享元模式的缺点
(1)关注内、外部状态,关注线程安全问题。
(2)使系统、程序的逻辑复杂化。
_
(end)