Context接口的设计

时间:2016-11-17 12:13:41

标签: go

我的问题是关于Context interface的设计选择。如果我想从child创建parent上下文,我可以执行以下操作:

child, cancel := context.WithTimeout(parent, timeout)

如果WithTimeout是界面的一部分会不会更好,所以我们可以简单地写一下:

child, cancel := parent.WithTimeout(timeout)

对我来说似乎更清洁。它更短,不需要import context

为什么生成子上下文的函数不属于Context接口?

2 个答案:

答案 0 :(得分:6)

这是context.Context类型:

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}

很简单。如果你要编写它的实现,你能做到吗?是的,很容易。因为没有&#34; setter&#34;方法,每个方法只能返回一个默认值/ zero value,它是一个有效的&#34;实现。这正是背景和TODO背景所做的事情(context.Background()context.TODO())。

如果要添加从现有的派生新函数的函数(例如context.WithCancel()context.WithDeadline()等)作为Context接口的一部分本身,需要为所有人提供(有效)实施,这是不可行的;并且很少需要同时进行所有这些,因此浪费资源。

扩展程序负责添加实现。如果您查看context包的实施方式:context/context.go,您会看到不同的context.Context实施不同的&#34;衍生品&#34;或&#34;扩展&#34;:

// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int


// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
    Context

    done chan struct{} // closed by the first cancel call.

    mu       sync.Mutex
    children map[canceler]bool // set to nil by the first cancel call
    err      error             // set to non-nil by the first cancel call
}

// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {
    cancelCtx
    timer *time.Timer // Under cancelCtx.mu.

    deadline time.Time
}

// A valueCtx carries a key-value pair. It implements Value for that key and
// delegates all other calls to the embedded Context.
type valueCtx struct {
    Context
    key, val interface{}
}

显然,我们可以为不在context.Context包中的context组成其他有用的扩展程序。如果您有新想法,是否还要将其添加到Context界面? 这会破坏所有现有的实施,因为显然你的新想法并未在其他实施中实现。目前的实施。

答案 1 :(得分:1)

恕我直言,有两个原因:

  1. 这是因为WithContextparent无关 - 例如父母不应该也不应该对你可以从中创建子上下文这一事实有任何想法。在Go ideology中,界面应该尽可能小。

  2. 它更具可读性和清晰度,您将获得什么作为输出。在当前的实施中,someVar, _ := context.WithTimeout(value)被称为some variable is a new (:=) context with a timeout。在您建议的版本中,它someVar, _ := parent.WithTimeout(value)有点模糊。