:::tips 本质上, 工厂模式是为了解决 相似类初始化和初始化十分复杂的情况
:::
使用场景
我们来看一下这样的场景, 现在需要翻译接口, 能够翻译, 大概会写出下面这样的代码
package main
import (
"fmt"
"time"
)
//翻译接口
type Translator interface {
Translate(string) string
}
//德语翻译类
type GermanTranslator struct{}
func (*GermanTranslator) Translate(words string) string {
return "德语"
}
//英语翻译类
type EnglishTranslator struct{}
func (*EnglishTranslator) Translate(words string) string {
return "英语"
}
//日语翻译类
type JapaneseTranslator struct{}
func (*JapaneseTranslator) Translate(words string) string {
return "日语"
}
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
time.Sleep(3 * time.Second)
}()
var lan int
fmt.Printf("%s\r\n%s\r\n", "以下是可翻译的语言种类,请输入代表数字", "1:德语、2:英语、3:日语")
fmt.Scanln(&lan)
fmt.Println("请输入要翻译成中文的文本:")
var inputWords string
fmt.Scanln(&inputWords)
var translator Translator
//根据不同的语言种类,实例化不同的翻译类
switch lan {
case 1:
translator = new(GermanTranslator)
case 2:
translator = new(EnglishTranslator)
case 3:
translator = new(JapaneseTranslator)
default:
panic("no such translator")
}
fmt.Println(translator.Translate(inputWords))
}
- 违背了
开闭原则
: 这样创建类, 如果添加了其他翻译类, 每次都需要更改客户端代码, 一次修改, 处处漏水 - 违背了
单一职责
: 客户端应该只接受用户输入, 然后返回输出, 现在还需要负责类的创建
简单工厂模式
golang
我们使用简单工厂模式进行改写
package main
import (
"fmt"
"time"
)
//翻译接口
type Translator interface {
Translate(string) string
}
func CreatTranslator(lan int) Translator {
var translator Translator
switch lan {
case 1: translator = new(GermanTranslator)
case 2: translator = new(EnglishTranslator)
case 3: translator = new(JapaneseTranslator)
}
return translator
}
//德语翻译类
type GermanTranslator struct{}
func (*GermanTranslator) Translate(words string) string {
return "德语"
}
//英语翻译类
type EnglishTranslator struct{}
func (*EnglishTranslator) Translate(words string) string {
return "英语"
}
//日语翻译类
type JapaneseTranslator struct{}
func (*JapaneseTranslator) Translate(words string) string {
return "日语"
}
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
time.Sleep(3 * time.Second)
}()
var lan int
fmt.Printf("%s\r\n%s\r\n", "以下是可翻译的语言种类,请输入代表数字", "1:德语、2:英语、3:日语")
fmt.Scanln(&lan)
fmt.Println("请输入要翻译成中文的文本:")
var inputWords string
fmt.Scanln(&inputWords)
translator := CreatTranslator(lan)
fmt.Println(translator.Translate(inputWords))
}
- 上面还需要检测工厂产出的是否成功, 这里没有写
- 改写后, 职责划分更加清晰, 业务代码中不再负责类创建, 符合了
单一职责原则
- 但是, 新增类的时候依然需要修改整个工厂类, 不符合
开闭原则
, 不过也需要根据场景来进行取舍, 如果这里不会增加太多新方法, 那么即便不符合也死能够接受的, 因为要完全符合原则, 会牺牲代码可读性
提高代码复杂度
工厂模式
golang
golang没有继承, 基于匿名组合完成
package main
import (
"fmt"
"time"
)
// Translator 翻译接口
type Translator interface {
Translate(string) string
}
// TranslatorFactory 工厂接口
type TranslatorFactory interface {
Create() Translator
}
// TranslatorBase 是 Translator接口实现的基类, 封装公用方法
type TranslatorBase struct {
}
func (*TranslatorBase) PublicMethod() string {
return "public"
}
// GermanTranslatorFactory 德语翻译类工厂
type GermanTranslatorFactory struct{}
func (GermanTranslatorFactory) Create() Translator {
return &GermanTranslator{
TranslatorBase:&TranslatorBase{},
}
}
// GermanTranslator 德语翻译类
type GermanTranslator struct{
*TranslatorBase
}
func (g *GermanTranslator) Translate(words string) string {
g.PublicMethod()
return "德语"
}
// EnglishTranslatorFactory 英语翻译类工厂
type EnglishTranslatorFactory struct{}
func (EnglishTranslatorFactory) Create() Translator {
return &EnglishTranslator{
TranslatorBase:&TranslatorBase{},
}
}
// EnglishTranslator 英语翻译类
type EnglishTranslator struct{
*TranslatorBase
}
func (e *EnglishTranslator) Translate(words string) string {
e.PublicMethod()
return "英语"
}
// JapaneseTranslatorFactory 日语翻译类工厂
type JapaneseTranslatorFactory struct{}
func (JapaneseTranslatorFactory) Create() Translator {
return &JapaneseTranslator{
TranslatorBase:&TranslatorBase{},
}
}
// JapaneseTranslator 日语翻译类
type JapaneseTranslator struct{
*TranslatorBase
}
func (j *JapaneseTranslator) Translate(words string) string {
j.PublicMethod()
return "日语"
}
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
time.Sleep(3 * time.Second)
}()
var lan int
fmt.Printf("%s\r\n%s\r\n", "以下是可翻译的语言种类,请输入代表数字", "1:德语、2:英语、3:日语")
fmt.Scanln(&lan)
fmt.Println("请输入要翻译成中文的文本:")
var inputWords string
fmt.Scanln(&inputWords)
switch lan {
case 1: TranslatorFuc(GermanTranslatorFactory{}, "1")
case 2: TranslatorFuc(EnglishTranslatorFactory{}, "2")
case 3: TranslatorFuc(JapaneseTranslatorFactory{}, "3")
}
}
func TranslatorFuc(factory TranslatorFactory, input string){
fmt.Println(factory.Create().Translate(input))
}
- 工厂模式多了很多代码, 主要每一个类都需要一个工厂, 这里其实导致了类的膨胀, 过多的类给维护带来了麻烦
- 在构造并不复杂的情况下, 工厂模式其实不如简单工厂来得直接
- 并且这里使用工厂之后, 逻辑又被混入到业务代码中了, 这样是不行的, 你还需要封装出一个创建工厂的工厂
:::tips 这里就体现了过度设计的弊端, 如果构造类并不复杂, 很多时候简单工厂比工厂要实用很多
:::