创建型设计模式——工厂方法模式

文章目录

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

工厂方法模式适合应用场景:

  • 当你在编写代码的过程中,如果无法预知对象确切类别及其依赖关系时,可使用工厂方法。
  • 如果你希望用户能扩展你软件库或框架的内部组件, 可使用工厂方法。
  • 如果你希望复用现有对象来节省系统资源,而不是每次都重新创建对象,可使用工厂方法。

问题

假设我们的业务需要一个支付渠道,我们开发了一个 Pay 方法,其可以用于支付。请看以下示例:

package main

import "fmt"

type Pay interface {
	Pay() string
}

type PayReq struct {
	OrderId string // 订单号
}

func (p *PayReq) Pay() string {
	// todo
	fmt.Println(p.OrderId)
	return "支付成功"
}

func main() {
	p := PayReq{OrderId: "12345678"}
	fmt.Println(p.Pay())
}

运行示例

如上,我们定义了接口 Pay,并实现了其方法 Pay()

如果业务需求变更,需要我们提供多种支付方式,一种叫 APay,一种叫 BPay,这二种支付方式所需的参数不同,APay 只需要订单号 OrderIdBPay 则需要订单号 OrderIdUid。此时如何修改?

很容易想到的是在原有的代码基础上修改,比如:

package main

import "fmt"

type Pay interface {
	APay() string
	BPay() string
}

type PayReq struct {
	OrderId string // 订单号
	Uid     int64
}

func (p *PayReq) APay() string {
	// todo
	fmt.Println(p.OrderId)
	return "APay支付成功"
}

func (p *PayReq) BPay() string {
	// todo
	fmt.Println(p.OrderId)
	fmt.Println(p.Uid)
	return "BPay支付成功"
}

func main() {
	a := &PayReq{OrderId: "A12345678"}
	fmt.Println(a.APay())

	b := &PayReq{OrderId: "B12345678", Uid: 888}
	fmt.Println(b.BPay())
}

运行示例

我们为 Pay 接口实现了 APay()BPay() 方法。虽然暂时实现了业务需求,但却使得结构体 PayReq 变得冗余了,APay() 并不需要 Uid 参数。如果之后再增加CPayDPayEPay,可想而知,代码会变得越来越难以维护。随着后续业务迭代,将不得不编写出复杂的代码。

解决

让我们想象一个工厂类,这个工厂类需要生产电线和开关等器具,我们可以为工厂类提供一个生产方法,当电线机器调用生产方法时,就产出电线,当开关机器调用生产方法时,就产出开关。

套用到我们的支付业务来,就是我们不再为接口提供 APay 方法、BPay 方法,而只提供一个 Pay 方法,并将 A 支付方式和 B 支付方式的区别下放到子类。

package main

import "fmt"

type Pay interface {
	Pay() string
}

type PayReq struct {
	OrderId string
}

type APayReq struct {
	PayReq
}

func (p *APayReq) Pay() string {
	// todo
	fmt.Println(p.OrderId)
	return "APay支付成功"
}

type BPayReq struct {
	PayReq
	Uid int64
}

func (p *BPayReq) Pay() string {
	// todo
	fmt.Println(p.OrderId)
	fmt.Println(p.Uid)
	return "BPay支付成功"
}

func main() {
	var pay Pay
	pay = &APayReq{PayReq{"A12345678"}}
	fmt.Println(pay.Pay())

	pay = &BPayReq{PayReq: PayReq{"B12345678"}, Uid: 888}
	fmt.Println(pay.Pay())
}

运行示例

我们用 APayBPay 两个结构体实现了 Pay() 方法,如果需要添加一种新的支付方式,只需要实现新类型的 Pay() 方法即可。

工厂方法的优点就在于避免了创建者和具体产品之间的紧密耦合,从而使得代码更容易维护。

参考文章:


也可以看看


全国大流量卡免费领

19元月租ㆍ超值优惠ㆍ长期套餐ㆍ免费包邮ㆍ官方正品