go设计模式之简单工厂浅谈|Go主题月

239 阅读3分钟

工厂模式

1.简单工厂

//定义两个生成动物的工厂,一个生成Dog,一个生成Cat
type Cat struct{}
type Dog struct{}
//定义一个接口,为了代码简单,这里我定义了一个空接口,这样Dog,Cat就不用实现任何方法
type Animal interface{}
//简单工厂方法,用于生成具体动物
func Create(animalType string) Animal {
    var animal Animal
    switch animalType {
    case "cat":
        animal = &Cat{}
    case "dog":
        animal = &Dog{}
    }
    return animal
}

以上就是一个简单工厂模式 使用如下:

cat := Create("cat")
dog := Create("dog")

一切看上去好像很完美,但是当我们需要新加入一种动物(即新添加一个工厂)时,我们还是需要修改两处地方,修改如下

//第一处修改,新建一个动物结构(如duck)
type Duck struct{}
//第二处修改
func Create(animalType string) Animal {
    var animal Animal
    switch animalType {
    case "cat":
        animal = &Cat{}
    case "dog":
        animal = &Dog{}
    case "duck":        //++++此处为新增逻辑
        animal = &Duck{}
    }
    return animal
}

修改Create方法并不是明智之举,很明显违背了设计模式的开闭原则,那有什么方法加以改善??? 由于本人也是刚接触go,在这里提出以下想法,写得不好,或者有什么问题,希望各位看官指正,经过修改代码如下

//定义两个生成动物的工厂,一个生成Dog,一个生成Cat
type Cat struct{}
type Dog struct{}
//定义一个接口
type Animal interface{}
//Cat的构造函数
func CreateCat() Animal{
	retrun &Cat{}
}
//Dog的构造函数
func CreateDog() Animal{
	retrun &Dog{}
}
//定义一个工厂结构
//结构里面只有一个类型为[string]func() Animal的map,它主要是用来存放各种动物的”构造函数“
type Factry struct {
	animals map[string]func() Animal
}

为了描述方便我们可以把func() Animal定义为CreateFn

type CreateFn func() Animal
//于是Factry可以改写为
type Factry struct{
	animals map[string]CreateFn
}

定义一个newFactry方法,方法主要用于生成Factry实列,并且构建animals,目的是为了构建一个描述动物关键字(如:”cat"代表Cate,"dog"代表Dog)与各动物”构造函数“的映射

func newFactry() *Factry {
	return &Factry{
		animals: map[string]CreateFn{
			"dog": CreateDog,//把"dog"关键字映射到构造函数CreateDog
			"cat": CreateCat,
		},
	}
}

至此主要逻辑就已经完成了,剩下怎么创建具体动物这一步了,我们给Factry结构体添加一个Create方法方法,如下:

func (f *Factry) Create(name string) Animal {
	//name为关键字,通过关键字,我们可以从animals找到对应的构造函数
	return f.animals[name]()
}

大功告成

至此工厂方法已经好了,那怎么使用列?如下:

fac := newFactry()         //新建一个工厂
dog := fac.Create("dog")//新建一个dog
cat := fac.Create("cat")  //新建一个cat

总结:通过以上改造,我们解决了简单工厂添加新工厂存在的问题,即需要添加新分支:

case "duck":        //++++此处为新增逻辑
	animal = &Duck{}

因为,我们统一把分支管理交给了工厂结构体里的animals map[string]CreateFn来管理,这样添加新动物时,我们只需要专心写我们的动物结构就可以了,写好后在animals 添加对应关系即可,

继续改善

我们使用一个map来保存构造函数就可以避免使用Factry结构体

type CreateFn func() Animal
var animals map[string]CreateFn ={
    "dog": CreateDog,//把"dog"关键字映射到构造函数CreateDog
    "cat": CreateCat,
}

使用

dog := animals["dog"]()
cat := animals["cat"]()

新增duck只需在animals添加对应的映射就可以了

var animals map[string]CreateFn ={
    "dog": CreateDog,//把"dog"关键字映射到构造函数CreateDog
    "cat": CreateCat,
    "duck":CreateDuck,
}

//使用新结构duck
duck := animals["duck"}()

总结,简单工厂每次新添加结构,都需要修改创建逻辑,违背开闭原则,可以通过工厂方法解决查看我的下一篇文章go设计模式之工厂方法浅谈|Go主题月