创建型模式-工厂方法

什么是工厂方法

工厂方法是一种创建型设计模式, 其在父类中提供一个创建对象的  方法,允许子类决定实例化对象 的类型

Example

产品接口

package main

type IGun interface {
setName(name string)
setPower(power int)
getName() string
getPower() int
}

具体产品A

package main

type Gun struct {
name string
power int
}

func (g *Gun) setName(name string) {
g.name = name
}

func (g *Gun) getName() string {
return g.name
}

func (g *Gun) setPower(power int) {
g.power = power
}

func (g *Gun) getPower() int {
return g.power
}

具体产品B

package main

type Ak47 struct {
Gun
}

func newAk47() IGun {
return &Ak47{
Gun: Gun{
name: "AK47 gun",
power: 4,
},
}
}

具体产品C

package main

type musket struct {
Gun
}

func newMusket() IGun {
return &musket{
Gun: Gun{
name: "Musket gun",
power: 1,
},
}
}

工厂

package main

import "fmt"

func getGun(gunType string) (IGun, error) {
if gunType == "ak47" {
return newAk47(), nil
}
if gunType == "musket" {
return newMusket(), nil
}
return nil, fmt.Errorf("Wrong gun type passed")
}

客户端

package main

import "fmt"

func main() {
ak47, _ := getGun("ak47")
musket, _ := getGun("musket")

printDetails(ak47)
printDetails(musket)
}

func printDetails(g IGun) {
fmt.Printf("Gun: %s", g.getName())
fmt.Println()
fmt.Printf("Power: %d", g.getPower())
fmt.Println()
}

适用场景

1.当你在编写代码的过程中,如果无法预知对象确切类别及其 依赖关系时,可使用工厂方法。

  • 例如,如果需要向应用中添加一种新产品,你只需要开发新 的创建者子类,然后重写其工厂方法即可
  1. 如果你希望用户能扩展你软件库或框架的内部组件,可使用 工厂方法
  • 解决方案是将各框架中构造组件的代码集中到单个工厂方法 中,并在继承该组件之外允许任何人对该方法进行重写
  1. 如果你希望复用现有对象来节省系统资源,而不是每次都重 新创建对象,可使用工厂方法

优缺点

优点

  • 避免创建者和具体产品的耦合

  • 单一职责原则。你可以将产品创建代码放在程序的单一位置, 从而使得代码更容易维护

  • 开闭原则。无需更改现有客户端代码,你就可以在程序中引 入新的产品类型。

缺点

  • 应用工厂方法模式需要引入许多新的子类,代码可能会因此 变得更复杂。最好的情况是将该模式引入创建者类的现有层 次结构中

与其他模式的关系

  • 在许多设计工作的初期都会使用工厂方法(较为简单,而且 可以更方便地通过子类进行定制),随后演化为使用抽象工 厂、原型或生成器(更灵活但更加复杂)
  • • 抽象工厂模式通常基于一组工厂方法,但你也可以使用原型 模式来生成这些类的方法
  • 你可以同时使用工厂方法和迭代器来让子类集合返回不同类 型的迭代器,并使得迭代器与集合相匹配。
  • • 原型并不基于继承,因此没有继承的缺点。另一方面,原型 需要对被复制对象进行复杂的初始化。 工厂方法基于继承, 但是它不需要初始化步骤。
  • 工厂方法是模板方法的一种特殊形式。同时,工厂方法可以 作为一个大型模板方法中的一个步骤