Go 中的功能选项:在 Golang 中实现 Options 模式

文章目录

本文介绍了Go中的功能选项以及如何使用Options模式实现它们。

功能选项采用函数额外参数的形式,扩展或修改其行为。以下是一个使用功能选项创建新的House结构体的示例:

h := NewHouse(
  WithConcrete(),
  WithoutFireplace(),
)

在这里,NewHouse 是一个构造函数。 WithConcrete 和 WithFireplace 传递给构造函数的选项,以修改其返回值。

我们很快会看到为什么WithConcrete和WithFireplace被称为“函数”选项,以及它们比常规函数参数更有用的原因。

定义构造函数

首先,定义我们将为其设置选项的结构:

type House struct {
	Material     string
	HasFireplace bool
	Floors       int
}

// `NewHouse`是一个用于`*House`的构造函数
func NewHouse() *House {
	const (
		defaultFloors       = 2
		defaultHasFireplace = true
		defaultMaterial     = "wood"
	)

	h := &House{
		Material:     defaultMaterial,
		HasFireplace: defaultHasFireplace,
		Floors:       defaultFloors,
	}

	return h
}

House可以由特定材料制成,可以有一定数量的楼层,并且可以选择包含壁炉。

NewHouse构造函数给我们默认的House指针,其所有属性都具有某些默认值。

通常,我们需要首先构造房子,然后如果需要不同的变体,则修改其值。使用功能选项,我们可以将一系列修改直接提供给构造函数。

定义功能选项

让我们定义一个接受指向 House 的指针的函数类型:

type HouseOption func(*House)

这是我们选项函数的签名。

让我们定义一些功能选项,可以修改 *House 实例:

func WithConcrete() HouseOption {
	return func(h *House) {
		h.Material = "concrete"
	}
}

func WithoutFireplace() HouseOption {
	return func(h *House) {
		h.HasFireplace = false
	}
}

上述每个函数都是“选项构造函数”并返回另一个函数,该函数以 *House 作为参数,并且不返回任何内容。

我们可以看到返回的函数修改了提供的 *House 实例。

我们甚至可以在选项构造器中添加参数,以修改返回的选项:

func WithFloors(floors int) HouseOption {
	return func(h *House) {
		h.Floors = floors
	}
}

这将返回按 WithFloors 选项构造器给定的参数修改房屋中楼层数的选项。

将功能选项添加到我们的构造函数

现在,我们可以将功能选项合并到构造函数中:

// NewHouse现在将一个选项切片作为其余参数
func NewHouse(opts ...HouseOption) *House {
	const (
		defaultFloors       = 2
		defaultHasFireplace = true
		defaultMaterial     = "wood"
	)

	h := &House{
		Material:     defaultMaterial,
		HasFireplace: defaultHasFireplace,
		Floors:       defaultFloors,
	}

	// 循环每个选项
	for _, opt := range opts {
		// 调用选项,通过实例化*House作为参数
		opt(h)
	}

	// 返回修改后的house实例
	return h
}

构造函数现在接受任意数量的函数选项参数列表,然后在返回之前将其应用于 *House 实例。回到第一个例子,我们现在可以理解这些选项的作用:

h := NewHouse(
  WithConcrete(),
  WithoutFireplace(),
  WithFloors(3),
)

使用选项模式的优点

现在我们已经了解了如何实现选项模式,让我们看看为什么要使用函数选项。

显式

与以下方式修改 *House 不同:

h := NewHouse()
h.Material = "concrete"

我们可以在构造函数本身中明确建筑材料:

h := NewHouse(WithConcrete())

这有助于我们明确材料的字符串值。前面的示例允许用户制造拼写错误并暴露 *House 实例的内部。

可扩展

如果我们确实想给用户可扩展性,我们可以始终为我们的选项构造函数提供参数。

例如,由于我们的房屋楼层可以有任意数量,因此可以通过提供楼层数的参数来创建向房屋添加楼层的选项:

h := NewHouse(WithFloors(4))

参数的顺序

在使用功能选项时,选项的顺序不重要。与常规函数参数相比(必须以正确的顺序提供),这为我们提供了很大的灵活性。

此外,我们可以提供任意数量的选项。当使用带有常规参数的函数时,我们必须提供所有参数:

// 如果使用常规函数参数,则`NewHouse`看起来像什么
// 无论如何,我们总是需要提供所有三个参数
h := NewHouse("concrete", 5, true)

最后,祝您编码愉快!


也可以看看


全国大流量卡免费领

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