缘起

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

策略模式

  1. 策略模式(Strategy Pattern)又叫作政策模式(Policy Pattern),
  2. 它将定义的算法家族分别封装起来,
  3. 让它们之间可以互相替换,
  4. 从而让算法的变化不会影响到使用算法的用户,
  5. 属于行为型设计模式。
  6. (摘自 谭勇德 <<设计模式就该这样学>>)

场景

  • 某学员成绩管理系统, 需要对学员成绩进行排序
  • 码农王二狗根据<<我的第一本算法书>>里的描述, 使用冒泡排序算法实现了成绩排序功能
  • Leader审查代码时, 认为王二狗的实现虽然完成了功能, 但不够sexy, 要求改进
  • 于是王二狗继续翻查算法书, 又加上了选择排序和快速排序算法.
  • 为兼容性和可扩展性考虑, 王二狗根据策略模式, 把排序算法都抽象成统一接口, 等Leader说哪个好, 就用那个.

设计

  • ISortPolicy: 排序算法接口
  • tBubbleSortPolicy: 冒泡排序算法, 实现ISortPolicy接口
  • tSelectSortPolicy: 选择排序算法, 实现ISortPolicy接口
  • tQuickSortPolicy: 快速排序算法, 实现ISortPolicy接口. 内部顺便实现了一个LIFO栈 - tIntStack.

单元测试

policy_pattern_test.go, 生成若干随机整数, 然后分别使用冒泡, 选择, 快速排序算法进行排序

  1. package behavioral_patterns
  2. import (
  3. plc "learning/gooop/behavioral_patterns/policy"
  4. "math/rand"
  5. "testing"
  6. "time"
  7. )
  8. func Test_PolicyPattern(t *testing.T) {
  9. size := 20
  10. data := make([]int, size)
  11. r := rand.New(rand.NewSource(time.Now().Unix()))
  12. for i, _ := range data {
  13. data[i] = r.Intn(100)
  14. }
  15. t.Logf("UnsortedData \t= %v", data)
  16. fnMakeCopy := func() []int{
  17. copies := make([]int, size)
  18. for i,v := range data {
  19. copies[i] = v
  20. }
  21. return copies
  22. }
  23. fnTestPolicy := func(policy plc.ISortPolicy) {
  24. sorted := policy.Sort(fnMakeCopy())
  25. t.Logf("%s \t= %v", policy.Name(), sorted)
  26. }
  27. fnTestPolicy(plc.NewBubbleSortPolicy())
  28. fnTestPolicy(plc.NewSelectSortPolicy())
  29. fnTestPolicy(plc.NewQuickSortPolicy())
  30. }

测试输出

  1. $ go test -v policy_pattern_test.go
  2. === RUN Test_PolicyPattern
  3. policy_pattern_test.go:17: UnsortedData = [19 17 28 36 80 84 70 7 80 68 2 96 94 26 22 31 80 49 49 69]
  4. policy_pattern_test.go:29: BubbleSort = [2 7 17 19 22 26 28 31 36 49 49 68 69 70 80 80 80 84 94 96]
  5. policy_pattern_test.go:29: SelectSort = [2 7 17 19 22 26 28 31 36 49 49 68 69 70 80 80 80 84 94 96]
  6. policy_pattern_test.go:29: QuickSort = [2 7 17 19 22 26 28 31 36 49 49 68 69 70 80 80 80 84 94 96]
  7. --- PASS: Test_PolicyPattern (0.00s)
  8. PASS
  9. ok command-line-arguments 0.002s

ISortPolicy.go

排序算法接口

  1. package policy
  2. type ISortPolicy interface {
  3. Name() string
  4. Sort(data []int) []int
  5. }

tBubbleSortPolicy.go

冒泡排序算法, 实现ISortPolicy接口

  1. package policy
  2. // 冒泡排序
  3. type tBubbleSortPolicy struct {
  4. }
  5. func NewBubbleSortPolicy() ISortPolicy {
  6. return &tBubbleSortPolicy{}
  7. }
  8. func (self *tBubbleSortPolicy) Name() string {
  9. return "BubbleSort"
  10. }
  11. func (self *tBubbleSortPolicy) Sort(data []int) []int {
  12. if data == nil {
  13. return nil
  14. }
  15. size := len(data)
  16. if size <= 1 {
  17. return data
  18. }
  19. for {
  20. i := size - 1
  21. changed := false
  22. for {
  23. if i <= 0 {
  24. break
  25. }
  26. j := i - 1
  27. if data[j] > data[i] {
  28. data[i],data[j] = data[j],data[i]
  29. changed = true
  30. }
  31. i--
  32. }
  33. if !changed {
  34. break
  35. }
  36. }
  37. return data
  38. }

tSelectSortPolicy.go

选择排序算法, 实现ISortPolicy接口

  1. package policy
  2. // 选择排序
  3. type tSelectSortPolicy struct {
  4. }
  5. func NewSelectSortPolicy() ISortPolicy {
  6. return &tSelectSortPolicy{}
  7. }
  8. func (self *tSelectSortPolicy) Name() string {
  9. return "SelectSort"
  10. }
  11. func (self *tSelectSortPolicy) Sort(data []int) []int {
  12. if data == nil {
  13. return nil
  14. }
  15. size := len(data)
  16. if size <= 1 {
  17. return data
  18. }
  19. i := 0
  20. for {
  21. if i >= size - 1 {
  22. break
  23. }
  24. p, m := self.min(data, i + 1, size - 1)
  25. if m < data[i] {
  26. data[p], data[i] = data[i], data[p]
  27. }
  28. i++
  29. }
  30. return data
  31. }
  32. func (self *tSelectSortPolicy) min(data []int, from int, to int) (p int, v int) {
  33. i := from
  34. p = from
  35. v = data[from]
  36. if to <= from {
  37. return p, v
  38. }
  39. for {
  40. i++
  41. if i > to {
  42. break
  43. }
  44. if data[i] < v {
  45. p = i
  46. v = data[i]
  47. }
  48. }
  49. return p, v
  50. }

tQuickSortPolicy.go

快速排序算法, 实现ISortPolicy接口. 内部顺便实现了一个LIFO栈.

  1. package policy
  2. import "errors"
  3. // 快速排序
  4. type tQuickSortPolicy struct {
  5. mLeftStack *tIntStack
  6. mRightStack *tIntStack
  7. }
  8. func NewQuickSortPolicy() ISortPolicy {
  9. return &tQuickSortPolicy{
  10. newIntStack(),newIntStack(),
  11. }
  12. }
  13. // LIFO栈
  14. type tIntStack struct {
  15. tail *tIntNode
  16. size int
  17. }
  18. type tIntNode struct {
  19. Value int
  20. Prev *tIntNode
  21. }
  22. func newIntNode(value int) *tIntNode {
  23. return &tIntNode {
  24. value, nil,
  25. }
  26. }
  27. func newIntStack() *tIntStack {
  28. return &tIntStack{
  29. nil,
  30. 0,
  31. }
  32. }
  33. func (self *tIntStack) Push(i int) {
  34. node := newIntNode(i)
  35. node.Prev = self.tail
  36. self.tail = node
  37. self.size++
  38. }
  39. func (self *tIntStack) Pop() (error,int) {
  40. if self.size > 0 {
  41. self.size--
  42. node := self.tail
  43. self.tail = self.tail.Prev
  44. return nil, node.Value
  45. } else {
  46. return errors.New("empty stack"), 0
  47. }
  48. }
  49. func (self *tIntStack) Size() int {
  50. return self.size
  51. }
  52. func (self *tQuickSortPolicy) Name() string {
  53. return "QuickSort"
  54. }
  55. func (self *tQuickSortPolicy) Sort(data []int) []int {
  56. self.qsort(data, 0, len(data) - 1)
  57. return data
  58. }
  59. func (self *tQuickSortPolicy) qsort(data []int, from int, to int) {
  60. if to <= from {
  61. return
  62. }
  63. // only two
  64. if to == from + 1 {
  65. if data[to] < data[from] {
  66. data[from], data[to] = data[to], data[from]
  67. }
  68. return
  69. }
  70. // get pivot
  71. iPivot := (from + to) / 2
  72. vPivot := data[iPivot]
  73. // split left and right
  74. left := 0
  75. right := 0
  76. for i := from; i <= to; i++ {
  77. if i == iPivot {
  78. continue
  79. }
  80. v := data[i]
  81. if v <= vPivot {
  82. self.mLeftStack.Push(v)
  83. left++
  84. } else {
  85. self.mRightStack.Push(v)
  86. right++
  87. }
  88. }
  89. // pop right stack
  90. p := to
  91. for i := right; i > 0; i-- {
  92. e,v := self.mRightStack.Pop()
  93. if e != nil {
  94. panic(e)
  95. }
  96. data[p] = v
  97. p--
  98. }
  99. // pop pivot
  100. data[p] = vPivot
  101. p--
  102. // pop left stack
  103. for i := left; i > 0; i-- {
  104. e,v := self.mLeftStack.Pop()
  105. if e != nil {
  106. panic(e)
  107. }
  108. data[p] = v
  109. p--
  110. }
  111. // qsort left and right
  112. self.qsort(data, from, from + left - 1)
  113. self.qsort(data, to - right + 1, to)
  114. }

策略模式小结

  1. 策略模式的优点
  2. 1)策略模式符合开闭原则。
  3. 2)避免使用多重条件转移语句,如if...else语句、switch...case语句
  4. 3)使用策略模式可以提高算法的保密性和安全性。
  5. 策略模式的缺点
  6. 1)客户端必须知道所有的策略,并且自行决定使用哪一个策略类。
  7. 2)代码中会产生非常多策略类,增加维护难度。
  8. (摘自 谭勇德 <<设计模式就该这样学>>)

(end)