您可以防止创建无效的自定义类型吗?

时间:2019-09-06 12:58:32

标签: go

假设我们要创建一个自定义Probability类型以表示0到1之间的数字。我们可以这样做:

type Probability float64

func NewProbability(p float64) (*Probability, error) {
    if p < 0 || p > 1 {
        return nil, errors.New("Invalid Probability")
    }
    tmp := Probability(p)
    return &tmp, nil
}

只要我们代码的客户始终使用我们的NewProbability构造函数,此方法就起作用。但是他们可以通过类型转换解决它:

func main() {
    // works as intended
    p1, _ := NewProbability(0.5)
    fmt.Println(*p1)

    // errors as intended
    _, err := NewProbability(2)
    fmt.Println(err)

    // circumvents our constraints...
    // creates invalid Probability
    p3 := Probability(2)
    fmt.Println(p3)
}

https://play.golang.org/p/xJZQhkZLi_H

2个问题:

  1. 是否有任何方法可以阻止这种规避,因此,如果您拥有Probability,它将始终有效?
  2. 与主要问题无关:如果我在构造函数中省略了tmp变量,而是执行了return &Probability(p), nil,则会收到错误cannot take the address of Probability(p)Try it)。为什么在使用tmp变量时没有出现此错误?

3 个答案:

答案 0 :(得分:1)

  

您可以阻止创建无效的自定义类型吗?

否。

“古典” OOP语言提倡的想法是:如果防止滥用,就不会出现问题。如果用户不阅读您的文档,将会出现问题。

答案 1 :(得分:0)

1)您可以尝试将Probability构造为其中具有隐藏浮点值的结构,但是您将无法将其用作数字。另一个选择是将IsValid()方法添加到“概率”(有点类似于NaN)。

2)概率(p)是概率类型为p的副本。它是运算结果,在分配给变量之前,没有地址。将其分配给变量后,即可获取该变量的地址。

答案 2 :(得分:0)

每隔一段时间就会出现这个问题。 “确保”没有将无效值分配给某些自定义类型的唯一方法是在未导出的结构字段中使用getter和setter对其进行保护:

type Probability struct {
    p float64
}

func NewProbability(p float64) (Probability, error) {
    if p < 0 || p > 1 {
        return Probability{}, errors.New("invalid probability")
    }
    return Probability{p}
}

对于像浮标这样简单的东西,这可能是肿的过度杀伤力。较明智的方法通常是在每次接受这样的参数时检查您是否收到有效的概率:

func DidItHappen(p probability) (bool, error) {
    if p < 0 || p > 1 {
        return false, errors.New("invalid probability")
    }
    if /* roll the dice */ {
        return true, nil
    }
    return false, nil
}
相关问题