文章目录
这一章比较复杂,所以决定在此换一种讲述方式,按照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设计模式