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設計模式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章