方法包

包路径: github.com/caicloud/nirvana/definition/method

在 Nirvana 中,业务函数是 API 请求的 Handler,而且表现形式是函数或者同等形式的闭包。但是很多时候我们的业务函数可能是某个实例的方法。

为了保证业务与 API 定义的无关性,Nirvana 提供了 method 包,用于分离实例的创建和 API 定义。

API 定义:

var listMessages = def.Definition{
    Method:      def.List,
    Summary:     "List Messages",
    Description: "Query a specified number of messages and returns an array",
    Function:    method.Get(&message.Container{}, "ListMessages"),
    Parameters: []def.Parameter{
        {
            Source:      def.Query,
            Name:        "count",
            Default:     10,
            Description: "Number of messages",
        },
    },
    Results: def.DataErrorResults("A list of messages"),
}

业务方法:

// Container contains example title and content of messages.
type Container struct {
    Title   string
    Content string
}

// NewContainer creates Container
func NewContainer(title, content string) *Container {
    return &Container{title, content}
}

// ListMessages returns all messages.
func (m *Container) ListMessages(ctx context.Context, count int) ([]Message, error) {
    messages := make([]Message, count)
    for i := 0; i < count; i++ {
        messages[i].ID = i
        messages[i].Title = fmt.Sprintf("%s %d", m.Title, i)
        messages[i].Content = fmt.Sprintf("%s %d", m.Content, i)
    }
    return messages, nil
}

main.go 中创建实例并通过 Put() 函数放到方法容器中(需要在服务启动之前完成):

method.Put(message.NewContainer("Method Example", "Method Content"))

然后编译运行,访问 http://localhost:8080/apis/v1/messages 即可得到返回结果:

[{"id":0,"title":"Method Example 0","content":"Method Content 0"},{"id":1,"title":"Method Example 1","content":"Method Content 1"},{"id":2,"title":"Method Example 2","content":"Method Content 2"},{"id":3,"title":"Method Example 3","content":"Method Content 3"},{"id":4,"title":"Method Example 4","content":"Method Content 4"},{"id":5,"title":"Method Example 5","content":"Method Content 5"},{"id":6,"title":"Method Example 6","content":"Method Content 6"},{"id":7,"title":"Method Example 7","content":"Method Content 7"},{"id":8,"title":"Method Example 8","content":"Method Content 8"},{"id":9,"title":"Method Example 9","content":"Method Content 9"}]

method 包介绍

method 包是一个全局实例容器,每种类型对应一个实例。

具体实例类型

使用具体实例类型是一种比较常见的情况,比如上面的例子中提到的 *message.Container 实例。

其中 Put(ins insterface{}) 函数用于将一个实例放置到全局容器中,形成 类型-实例 的对应关系。

Get(typIns interface{}, method string) 用于生成一个匿名函数,生成的函数的签名没有方法的 receiver 部分,例如:

func (m *Container) ListMessages(ctx context.Context, count int) ([]Message, error)
生成匿名函数:
func (ctx context.Context, count int) ([]Message, error)

但是匿名函数的执行部分在创建时是不确定的,只有等到这个匿名函数第一次被调用的时候,才会去全局实例容器里找到对应类型的实例,然后调用指定的方法。

这就是 Get() 函数的实例延迟加载特性。一旦延迟加载完成后,之后即使全局实例容器里的实例发生改变,匿名函数仍然会调用旧的实例的方法。如果加载时对应的类型没有实例,则会 panic。

注意,Get() 函数的第一个参数仅仅用于获取类型,其实例值没有任何意义。因此不需要给这个实例的任何字段设置值。也就是说 &Container{}(*Container)(nil) 是一样的。

接口实例类型

除了直接使用具体的类型以外,还支持使用接口类型。和具体类型的区别只是需要明确的指定接口类型的指针。

设置接口实例:

method.PutInterface((*ArbitraryInterface)(nil), instance)

获取接口实例:

method.Get((*ArbitraryInterface)(nil), "MethodName")

results matching ""

    No results matching ""