:::tips 本质上, 工厂模式是为了解决 相似类初始化和初始化十分复杂的情况

:::

使用场景

我们来看一下这样的场景, 现在需要翻译接口, 能够翻译, 大概会写出下面这样的代码

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. //翻译接口
  7. type Translator interface {
  8. Translate(string) string
  9. }
  10. //德语翻译类
  11. type GermanTranslator struct{}
  12. func (*GermanTranslator) Translate(words string) string {
  13. return "德语"
  14. }
  15. //英语翻译类
  16. type EnglishTranslator struct{}
  17. func (*EnglishTranslator) Translate(words string) string {
  18. return "英语"
  19. }
  20. //日语翻译类
  21. type JapaneseTranslator struct{}
  22. func (*JapaneseTranslator) Translate(words string) string {
  23. return "日语"
  24. }
  25. func main() {
  26. defer func() {
  27. if err := recover(); err != nil {
  28. fmt.Println(err)
  29. }
  30. time.Sleep(3 * time.Second)
  31. }()
  32. var lan int
  33. fmt.Printf("%s\r\n%s\r\n", "以下是可翻译的语言种类,请输入代表数字", "1:德语、2:英语、3:日语")
  34. fmt.Scanln(&lan)
  35. fmt.Println("请输入要翻译成中文的文本:")
  36. var inputWords string
  37. fmt.Scanln(&inputWords)
  38. var translator Translator
  39. //根据不同的语言种类,实例化不同的翻译类
  40. switch lan {
  41. case 1:
  42. translator = new(GermanTranslator)
  43. case 2:
  44. translator = new(EnglishTranslator)
  45. case 3:
  46. translator = new(JapaneseTranslator)
  47. default:
  48. panic("no such translator")
  49. }
  50. fmt.Println(translator.Translate(inputWords))
  51. }
  • 违背了 开闭原则 : 这样创建类, 如果添加了其他翻译类, 每次都需要更改客户端代码, 一次修改, 处处漏水
  • 违背了 单一职责 : 客户端应该只接受用户输入, 然后返回输出, 现在还需要负责类的创建

简单工厂模式

golang

我们使用简单工厂模式进行改写

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. //翻译接口
  7. type Translator interface {
  8. Translate(string) string
  9. }
  10. func CreatTranslator(lan int) Translator {
  11. var translator Translator
  12. switch lan {
  13. case 1: translator = new(GermanTranslator)
  14. case 2: translator = new(EnglishTranslator)
  15. case 3: translator = new(JapaneseTranslator)
  16. }
  17. return translator
  18. }
  19. //德语翻译类
  20. type GermanTranslator struct{}
  21. func (*GermanTranslator) Translate(words string) string {
  22. return "德语"
  23. }
  24. //英语翻译类
  25. type EnglishTranslator struct{}
  26. func (*EnglishTranslator) Translate(words string) string {
  27. return "英语"
  28. }
  29. //日语翻译类
  30. type JapaneseTranslator struct{}
  31. func (*JapaneseTranslator) Translate(words string) string {
  32. return "日语"
  33. }
  34. func main() {
  35. defer func() {
  36. if err := recover(); err != nil {
  37. fmt.Println(err)
  38. }
  39. time.Sleep(3 * time.Second)
  40. }()
  41. var lan int
  42. fmt.Printf("%s\r\n%s\r\n", "以下是可翻译的语言种类,请输入代表数字", "1:德语、2:英语、3:日语")
  43. fmt.Scanln(&lan)
  44. fmt.Println("请输入要翻译成中文的文本:")
  45. var inputWords string
  46. fmt.Scanln(&inputWords)
  47. translator := CreatTranslator(lan)
  48. fmt.Println(translator.Translate(inputWords))
  49. }
  • 上面还需要检测工厂产出的是否成功, 这里没有写
  • 改写后, 职责划分更加清晰, 业务代码中不再负责类创建, 符合了 单一职责原则
  • 但是, 新增类的时候依然需要修改整个工厂类, 不符合 开闭原则 , 不过也需要根据场景来进行取舍, 如果这里不会增加太多新方法, 那么即便不符合也死能够接受的, 因为要完全符合原则, 会牺牲 代码可读性 提高代码复杂度

工厂模式

golang

golang没有继承, 基于匿名组合完成

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. // Translator 翻译接口
  7. type Translator interface {
  8. Translate(string) string
  9. }
  10. // TranslatorFactory 工厂接口
  11. type TranslatorFactory interface {
  12. Create() Translator
  13. }
  14. // TranslatorBase 是 Translator接口实现的基类, 封装公用方法
  15. type TranslatorBase struct {
  16. }
  17. func (*TranslatorBase) PublicMethod() string {
  18. return "public"
  19. }
  20. // GermanTranslatorFactory 德语翻译类工厂
  21. type GermanTranslatorFactory struct{}
  22. func (GermanTranslatorFactory) Create() Translator {
  23. return &GermanTranslator{
  24. TranslatorBase:&TranslatorBase{},
  25. }
  26. }
  27. // GermanTranslator 德语翻译类
  28. type GermanTranslator struct{
  29. *TranslatorBase
  30. }
  31. func (g *GermanTranslator) Translate(words string) string {
  32. g.PublicMethod()
  33. return "德语"
  34. }
  35. // EnglishTranslatorFactory 英语翻译类工厂
  36. type EnglishTranslatorFactory struct{}
  37. func (EnglishTranslatorFactory) Create() Translator {
  38. return &EnglishTranslator{
  39. TranslatorBase:&TranslatorBase{},
  40. }
  41. }
  42. // EnglishTranslator 英语翻译类
  43. type EnglishTranslator struct{
  44. *TranslatorBase
  45. }
  46. func (e *EnglishTranslator) Translate(words string) string {
  47. e.PublicMethod()
  48. return "英语"
  49. }
  50. // JapaneseTranslatorFactory 日语翻译类工厂
  51. type JapaneseTranslatorFactory struct{}
  52. func (JapaneseTranslatorFactory) Create() Translator {
  53. return &JapaneseTranslator{
  54. TranslatorBase:&TranslatorBase{},
  55. }
  56. }
  57. // JapaneseTranslator 日语翻译类
  58. type JapaneseTranslator struct{
  59. *TranslatorBase
  60. }
  61. func (j *JapaneseTranslator) Translate(words string) string {
  62. j.PublicMethod()
  63. return "日语"
  64. }
  65. func main() {
  66. defer func() {
  67. if err := recover(); err != nil {
  68. fmt.Println(err)
  69. }
  70. time.Sleep(3 * time.Second)
  71. }()
  72. var lan int
  73. fmt.Printf("%s\r\n%s\r\n", "以下是可翻译的语言种类,请输入代表数字", "1:德语、2:英语、3:日语")
  74. fmt.Scanln(&lan)
  75. fmt.Println("请输入要翻译成中文的文本:")
  76. var inputWords string
  77. fmt.Scanln(&inputWords)
  78. switch lan {
  79. case 1: TranslatorFuc(GermanTranslatorFactory{}, "1")
  80. case 2: TranslatorFuc(EnglishTranslatorFactory{}, "2")
  81. case 3: TranslatorFuc(JapaneseTranslatorFactory{}, "3")
  82. }
  83. }
  84. func TranslatorFuc(factory TranslatorFactory, input string){
  85. fmt.Println(factory.Create().Translate(input))
  86. }
  • 工厂模式多了很多代码, 主要每一个类都需要一个工厂, 这里其实导致了类的膨胀, 过多的类给维护带来了麻烦
  • 在构造并不复杂的情况下, 工厂模式其实不如简单工厂来得直接
  • 并且这里使用工厂之后, 逻辑又被混入到业务代码中了, 这样是不行的, 你还需要封装出一个创建工厂的工厂

:::tips 这里就体现了过度设计的弊端, 如果构造类并不复杂, 很多时候简单工厂比工厂要实用很多

:::