Go实现设计模式--工厂模式

这一章比较复杂,所以决定在此换一种讲述方式,按照Head First书中代码的道路走,为了方便运行,给出的代码全部是除了头文件以外的完整代码。

定义

定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

实例剖析

问题1 简单工厂的学习

你有一个披萨店,你的披萨店有orderPizza()函数,用于新建一个披萨,旧的代码如下(此处简化书上的代码)

orderPizze(ptype string)pizza{
   Pizza pizza
   if ptype=="a"{
     pizza = new(apizza)
   }else if ptype=="b{
     pizza = new(bpizza)
   }
   pizza.prepare()
   return pizza
}

分析1

新建pizza的流程
1.判断pizza类型
2.根据类型创建pizza
3.调用pizza对象的接口
4.返回pizza对象
改变:根据不同的类型,创建不同pizza对象是会改变的
不变:调用pizza接口,返回pizza的行为

行动1

将根据不同类型创建不同pizza的行为挪出来,放到另一个对象中,由这个对象专职创建pizza,这个新对象就称作工厂。

解释
在如下代码中
Pizza作为接口,各种不同的pizza可以实现不同的prepare方法
SimplePizzaFactory 是一个具体的工厂对象,他根据ptype的不同创建不同的pizza并返回
PizzaStore是我们的pizza商店,它不再关心pizza的具体诞生,它内含有工厂(可以有不同种类的工厂),每次点单,就将点单的参数传递给工厂,由工厂生产pizza,若工厂无能力生产所需要的pizza则返回空

type Pizza interface {
	prepare()
}

type CheesePizza struct {

}

func (s *CheesePizza)prepare(){
	fmt.Println("This is cheese pizza.")
}

type SimplePizzaFactory struct{
}

func (s *SimplePizzaFactory)CreatePizza(ptype string)Pizza{
	if ptype=="cheese"{
		return new(CheesePizza)
	}
	return nil
}


type PizzaStore struct {
  factory SimplePizzaFactory
}

func (s *PizzaStore)PizzaStore(factory SimplePizzaFactory){
	s.factory = factory
}

func (s *PizzaStore)orderPizza(ptype string)Pizza{
	pizza:=s.factory.CreatePizza(ptype)
	if pizza==nil{
		return nil
	}
	pizza.prepare()
	return pizza
}

func main() {
	factory:=new(SimplePizzaFactory)
	pizzaStore:=new(PizzaStore)
	pizzaStore.PizzaStore(*factory)
	pizzaStore.orderPizza("cheese")
	pizzaStore.orderPizza("test")
}

问题2 真正的工厂模式

在披萨店成功运行后,有了想要加盟披萨店的运营商,每个运营商生产的pizza会有不同特征(这个可以通过创建不同的工厂解决),在此之上,有的商家对于pizza生产的流程也希望可以进一步的扩展

分析2

核心思考:相同的需求,由子类,也就是哪一家披萨店决定了用户会吃到什么样的pizza

行动2

将CreatePizza上提到商店,将每个地方的商店看作是不同的工厂(问题一是商店依赖披萨工厂,此处是商店就是一个工厂),从而从那个商店点的pizza会有那个商店的特色,由子类决定用户会吃到什么样的pizza


type Pizza interface {
	prepare()
}

type PizzaStore interface {
	createPizza()
	orderPizza()
}
//不同地方的pizza对象
type ChicagoCheesePizza struct {
}

func (s *ChicagoCheesePizza)prepare(){
	fmt.Println("This is ChicagoCheese pizza.")
}

type NYCheesePizza struct {
}

func (s *NYCheesePizza)prepare(){
	fmt.Println("This is NYCheese pizza.")
}
//不同地方的经营商对象
type NYStypePizzaStore struct{
}

func (s *NYStypePizzaStore)CreatePizza(ptype string)Pizza{
	if ptype=="cheese"{
		return new(NYCheesePizza)
	}
	return nil
}


func (s *NYStypePizzaStore)orderPizza(ptype string)Pizza{
	var pizza Pizza
	if ptype=="cheese"{
		pizza = s.CreatePizza(ptype)
		pizza.prepare()
	}
	return pizza
}


type ChicagoStypePizzaStore struct{
}

func (s *ChicagoStypePizzaStore)CreatePizza(ptype string)Pizza{
	if ptype=="cheese"{
		return new(ChicagoCheesePizza)
	}
	return nil
}
//核心思维是由子类,也就是哪一家披萨店决定了用户会吃到什么样的pizza

func (s *ChicagoStypePizzaStore)orderPizza(ptype string)Pizza{
	var pizza Pizza
	if ptype=="cheese"{
		pizza = s.CreatePizza(ptype)
		pizza.prepare()
	}
	return pizza
}

func main() {
	nyStore:=new(NYStypePizzaStore)
	cstore:=new(ChicagoStypePizzaStore)
	pizza1:=nyStore.orderPizza("cheese")
	pizza2:=cstore.orderPizza("cheese")
	pizza1.prepare()
    pizza2.prepare()                                                                                                                                                                                        
}

对应关系图
在这里插入图片描述
思路方式
依赖倒置:
1.需要一个披萨店,那么披萨店内必然有各种各样的披萨
2.作为一个设计者,当然不希望关注具体的pizza,那么追寻它们的本质,他们都是披萨,所以可以抽象一个披萨
3.但是披萨店需要具体的披萨,那么我们可以做一个披萨工厂,将这些具体类去除披萨店,这样的话各类不同的披萨类型只能依赖一个抽象,而披萨商店也依赖这个抽象(披萨)。

问题3 抽象工厂模式

当有了不同地方的商店加入,为了保证披萨店的口碑,你需要限制披萨的原料,不同地区的同一种披萨可能需要的是不同的原料,这时候我们要做的就是创建一个原料工厂。

分析3

与披萨商店不同,披萨商店我们依赖的是与具体披萨一致的披萨类,此时创建披萨要获取原料,那么我们就需要在代码中加入原料工厂,但是每个地区原料工厂不一致,所以每个原料工厂依赖于一个抽象的原料类

行动3

1.创建抽象的原料工厂接口
2.根据接口实现不同的原料工厂类
3.具体的披萨依赖具体的原料工厂

type Dough interface {
	GetDough()
}

type Sause interface{
	GetSause()
}

type NYDough struct{

}

func (s *NYDough)GetDough(){
	fmt.Println("this is NYDough")
}

type NYSause struct{

}

func (s *NYSause)GetSause(){
	fmt.Println("this is NYSause")
}

type PizzaIngredientFactory interface{
	createDough()Dough
	createSause()Sause
}


type NYPizzaIngredientFactory struct{

}

func (s *NYPizzaIngredientFactory)createDough()Dough{
	return new(NYDough)
}

func (s *NYPizzaIngredientFactory)createSause()Sause{
	return new(NYSause)
}



type Pizza interface {
	prepare()
	getIngredientFactory(PizzaIngredientFactory)
}

type PizzaStore interface {
	createPizza()
	orderPizza()
}

type NYCheesePizza struct {
	pizzaIngredientFactory  PizzaIngredientFactory
	dough Dough
	sause Sause
}

func (s *NYCheesePizza)prepare(){
	s.sause = s.pizzaIngredientFactory.createSause()
	s.dough = s.pizzaIngredientFactory.createDough()
	s.dough.GetDough()
	s.sause.GetSause()
	fmt.Println("This is NYCheese pizza.")
}

func (s *NYCheesePizza)getIngredientFactory(factory PizzaIngredientFactory){
	s.pizzaIngredientFactory = factory
}
//不同地方的经营商对象
type NYStypePizzaStore struct{

}

func (s *NYStypePizzaStore)CreatePizza(ptype string)Pizza{
	pizzaIngredientFactory := new(NYPizzaIngredientFactory)
	var pizza Pizza
	if ptype=="cheese"{
		pizza = new(NYCheesePizza)
		pizza.getIngredientFactory(pizzaIngredientFactory)
	}
	return pizza
}


func (s *NYStypePizzaStore)orderPizza(ptype string)Pizza{
	var pizza Pizza
	if ptype=="cheese"{
		pizza = s.CreatePizza(ptype)
		pizza.prepare()
	}
	return pizza
}



func main() {
	nyStore:=new(NYStypePizzaStore)
	nyStore.orderPizza("cheese")
}

总结

工厂模式
通过子类来创建对象,由子类决定具体类型,负责将客户从具体类型中解耦。
如本文例子2中,芝加哥工厂和纽约工厂都是具体的对象,选择了工厂就决定了具体生产披萨。
抽象工厂模式
提供一个创建整个家族的抽象类型,这个类型的子类定义了产品被产生的方法。要想使用工厂必须先实例化它,然后传入一些针对抽象类型的所写的代码中。
如本文例子3中,披萨需要一个具体的原料工厂,我们先定义工厂抽象类,实现纽约原料工厂,并将其传入披萨内,这样的用法就是抽象工厂模式。

参考文章:
https://www.runoob.com/design-pattern/observer-pattern.html
Head First设计模式

发布了222 篇原创文章 · 获赞 35 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/hello_bravo_/article/details/104978619