這一章比較複雜,所以決定在此換一種講述方式,按照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設計模式