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

假设我们想要创建一个自定义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个问题:


有没有什么方法可以防止这种规避,这样即使有的话Probability它也总是有效的?

与主要问题无关:如果我tmp在构造函数中省略变量而是这样做return &Probability(p), nil,则会收到错误cannot take the address of Probability(p)(尝试一下)。为什么使用变量时tmp不会出现此错误?


交互式爱情
浏览 115回答 3
3回答

MMTTMM

您可以防止创建无效的自定义类型吗?不。“经典”OOP 语言所提倡的“如果防止误用就不会出现问题”的想法是没有根据的。如果用户不阅读您的文档就会出现问题。

湖上湖

1)您可以尝试将 Probability 设为一个包含隐藏浮点值的结构,但您将无法将其用作数字。另一种选择是将 IsValid() 方法添加到 Probability(有点类似于 NaN)。2) Probability(p) 是 p 的副本,类型为 Probability。它是一个运算结果的值,在分配给变量之前没有地址。当您将其分配给变量时,您可以获得该变量的地址。

慕桂英546537

这个问题每隔一段时间就会出现一次。“确保”不会将无效值分配给某些自定义类型的唯一方法是在未导出的结构体字段中使用 getter 和 setter 来保护它:type Probability struct {&nbsp; &nbsp; p float64}func NewProbability(p float64) (Probability, error) {&nbsp; &nbsp; if p < 0 || p > 1 {&nbsp; &nbsp; &nbsp; &nbsp; return Probability{}, errors.New("invalid probability")&nbsp; &nbsp; }&nbsp; &nbsp; return Probability{p}}对于像浮动这样简单的东西来说,这可能有点过分了。更明智的方法通常是检查每当您接受这样的参数时是否收到有效的概率:func DidItHappen(p probability) (bool, error) {&nbsp; &nbsp; if p < 0 || p > 1 {&nbsp; &nbsp; &nbsp; &nbsp; return false, errors.New("invalid probability")&nbsp; &nbsp; }&nbsp; &nbsp; if /* roll the dice */ {&nbsp; &nbsp; &nbsp; &nbsp; return true, nil&nbsp; &nbsp; }&nbsp; &nbsp; return false, nil}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go