工厂方法-Go

工厂方法——委托创建不同类型

工厂方法(或工厂)是工业实践中第二常用的设计模式。其目的是要达到特定的目标而需要实现的结构只是中将用户抽离出来(不是人话),例如从网络服务或者数据库中检索一些值。用户只需要一个获取值的接口。如果需要,还可以简化底层类型实现的降级或者升级过程(还不是人话)。

描述

使用Factory方法设计模式时,我们获得了额外的封装层,以便我们的程序可以在受控环境中增长。 使用Factory方法,我们将对象族的创建委派给不同的包或对象,以从我们可以使用的可能对象库的知识中抽象出来。 想象一下,您想使用旅行社安排假期。 您无需与酒店和旅行打交道,只需告诉旅行社您感兴趣的目的地,即可为您提供所需的一切。 旅行社代表旅行工厂。

目标

  • 将创建新的结构实例委托给程序的不同部分
  • 在接口级别工作,而不是具体的实现
  • 对对象族进行分组以获得族对象创建者

例子

在我们的示例中,将实现工厂付款方式,这将为我们提供在商店中付款的不同方式。首先,我们将有两种付款方式——现金和信用卡。我们还将有一个与方法Pay的接口,每个要用作支付方法的结构都必须实现。

接受标准如下:

  • 对称为“付款”的每种付款方式都有通用的方式
  • 为了能够将付款方式的创建委托给工厂
  • 只需将其添加到工厂方法即可将更多付款方法添加到库中

工厂方法非常简单,我们只要明确我们需要为接口存储多少实现,再提供一个可以传递支付方式类型的方法GetPaymentMethod即可。

type PaymentMethod interface {
	Pay(amount float32) string
}

上述代码定义了支付方法的接口,工厂方法会返回实现该接口的实例。

const (
	Cash      = 1
	DebitCard = 2
)

我们将工厂的支付方法定义成常量才能在包的外部调用、检查可能的支付方法。

为了完成工厂的定义,我们创建两个支付方法CashDebitCard。这两种支付方法结构体分别实现了PaymentMethod接口,返回包括支付内容的信息。

type CashPM struct{}
type DebitCardPM struct{}

func (c *CashPM) Pay(amount float32) string {
	return fmt.Sprintf("%0.2f paid using cash\n", amount)
}

func (d *DebitCardPM) Pay(amount float32) string {
	return fmt.Sprintf("%0.2f paid using debit card\n", amount)
}

接下来是为我们创建对象的方法,它返回一个指针,指向一个实现了PaymentMethod接口的对象,如果请求了未注册的支付方法则会返回一个错误。

func GetPaymentMethod(m int) (PaymentMethod, error) {
	switch m {
	case Cash:
		return new(CashPM), nil
	case DebitCard:
		return new(DebitCardPM), nil
	default:
		return nil, errors.New(fmt.Sprintf("Payment method %d not recognized\n", m))
	}
}

测试用例

首先创建测试用例,实现一个通用方法,取回实现PaymentMethod接口的对象。

GetPaymentMethod是一个取回支付方法的通用方法(不是人话),我们使用常量Cash作为参数(如果我们在包外使用该常量,我们需要带上包名factory.Cash)。检查err是否返回了错误,注意当我们获得了一个error的时候,调用了t.Fatal去停止测试运行,如果像之前几个测试中只使用t.Error的话,在下面尝试获取一个空对象的Pay方法时会出现问题,测试程序会崩溃退出。我们继续通过传递一个数值10.30给Pay方法,返回的信息仍旧包括paid using casht.Log(string)testing中的一个特殊方法,如果我们传递了-v参数则可以打印一些日志。

func TestCreatePaymentMethodCash(t *testing.T) {
	payment, err := GetPaymentMethod(Cash)
	if err != nil {
		t.Fatal("A payment method of type 'Cash' must exist")
	}

	msg := payment.Pay(10.30)
	if !strings.Contains(msg, "paid using cash") {
		t.Error("The cash payment method message was not correct")
	}
	t.Log("LOG: ", msg)
}

func TestCreatePaymentMethodDebitCard(t *testing.T) {
	payment, err := GetPaymentMethod(DebitCard)
	if err != nil {
		t.Fatal("A payment method of type 'DebitCard' must exist")
	}

	msg := payment.Pay(22.30)
	if !strings.Contains(msg, "paid using cash") {
		t.Error("The debit card payment method message was not correct")
	}
	t.Log("LOG: ", msg)
}

func TestGetPaymentMethodNonExistent(t *testing.T) {
	_, err := GetPaymentMethod(20)
	if err == nil {
		t.Error("A payment method with ID 20 must return an error")
	}
	t.Log("LOG: ", err)
}

测试结果如下:

$go test -v
=== RUN   TestCreatePaymentMethodCash
--- PASS: TestCreatePaymentMethodCash (0.00s)
    factory_test.go:18: LOG:  10.30 paid using cash

=== RUN   TestCreatePaymentMethodDebitCard
--- PASS: TestCreatePaymentMethodDebitCard (0.00s)
    factory_test.go:31: LOG:  22.30 paid using debit card

=== RUN   TestGetPaymentMethodNonExistent
--- PASS: TestGetPaymentMethodNonExistent (0.00s)
    factory_test.go:39: LOG:  Payment method 20 not recognized

PASS
ok      github.com/ricardoliu404/go-design-patterns/creational/factory  0.143s

源码见https://github.com/ricardoliu404/go-design-patterns/tree/master/creational/factory

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值