如何在Go中抽象出相同类型的循环细节?

时间:2015-05-07 10:01:44

标签: go

我有以下类型:

type entity struct {
    components []Component
}

func NewEntity(componentsLength ComponentType) Entity {
    return &entity{
        components: make([]Component, componentsLength),
    }
}

编码时,我注意到以下重复模式:

func (e *entity) HasComponent(components ...Component) bool {
    for _, c := range components {
        if e.components[c.Type()] == nil {
            return false
        }
    }
    return true
}

func (e *entity) HasAnyComponent(components ...Component) bool {
    for _, c := range components {
        if e.components[c.Type()] != nil {
            return true
        }
    }
    return false
}

当您为不同类型编写类似的函数时,使用大量重复代码似乎没问题。但是,在这种情况下,类型是相同的。我仍然无法重构代码以概括使用for循环和if语句。

如何重构这些函数以便列表处理很常见,但布尔运算符和值是否被抽象出来?我应该吗?

1 个答案:

答案 0 :(得分:0)

是的,我认为您应该重构此代码,不仅仅是为了使其更紧凑,而是为了更好地表达您正在执行的操作的集合理论性质。这两个函数都在询问有关e.componentscomponents的交集的问题。

HasComponent基本上要说的是HasAllComponents - 可能被称为e.components - 是componentscomponents的交集等于{{ 1}}。换句话说,交叉点的大小与components的大小相同。

至于HasAnyComponent,您所说的是十字路口的大小至少为一。

通过测试交叉点的大小,可以简洁地表达这两个函数,您可以使用具有此签名的函数来获取该函数:

func (e *entity) CountComponents(components ...Component) int

但是,您的原始代码具有短路逻辑,一旦知道答案,就会停止查看其他组件。您可以将这样一个短路循环合并到具有此签名的函数中:

func (e *entity) HasMinimumComponents(minimum int, components ...Component) bool

在循环中,每count递增一个e.components[c.Type()] != nil变量。 true尽快返回count >= minimum

现在,您可以使用单行正文有效地实现HasAllComponents

return entity.HasMinimumComponents(len(components), components...)

HasAnyComponent的身体变成了:

return entity.HasMinimumComponents(1, components...)

以下是一个用稍微不同的数据类型实现这个想法的程序。我定义了一个包含Entity的更抽象的map[int]bool。你应该毫不费力地将这个想法适应你自己的程序。

package main

import (
        "fmt"
)

type Entity struct {
        ValueMap map[int]bool
}

func MakeEntity(values ...int) *Entity {
        entity := Entity{map[int]bool{}}
        for _, value := range values {
                entity.ValueMap[value] = true
        }
        return &entity
}

func (entity *Entity) HasMinimumValues(minimum int, values ...int) bool {
        count := 0
        if minimum == 0 {
                return true
        }
        for _, value := range values {
                if entity.ValueMap[value] {
                        count++
                        if count >= minimum {
                                return true
                        }
                }
        }
        return false
}

func (entity *Entity) HasAllValues(values ...int) bool {
        return entity.HasMinimumValues(len(values), values...)
}

func (entity *Entity) HasAnyValue(values ...int) bool {
        return entity.HasMinimumValues(1, values...)
}

func main() {
        entity := MakeEntity(1, 3, 5, 7, 9)
        fmt.Printf("%t\n", entity.HasAllValues(3, 9))
        fmt.Printf("%t\n", entity.HasAllValues(3, 9, 12))
        fmt.Printf("%t\n", entity.HasAnyValue(9, 12, 15))
        fmt.Printf("%t\n", entity.HasAnyValue(12, 15))
}