方法包

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

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

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

API 定义:

  1. var listMessages = def.Definition{
  2. Method: def.List,
  3. Summary: "List Messages",
  4. Description: "Query a specified number of messages and returns an array",
  5. Function: method.Get(&message.Container{}, "ListMessages"),
  6. Parameters: []def.Parameter{
  7. {
  8. Source: def.Query,
  9. Name: "count",
  10. Default: 10,
  11. Description: "Number of messages",
  12. },
  13. },
  14. Results: def.DataErrorResults("A list of messages"),
  15. }

业务方法:

  1. // Container contains example title and content of messages.
  2. type Container struct {
  3. Title string
  4. Content string
  5. }
  6. // NewContainer creates Container
  7. func NewContainer(title, content string) *Container {
  8. return &Container{title, content}
  9. }
  10. // ListMessages returns all messages.
  11. func (m *Container) ListMessages(ctx context.Context, count int) ([]Message, error) {
  12. messages := make([]Message, count)
  13. for i := 0; i < count; i++ {
  14. messages[i].ID = i
  15. messages[i].Title = fmt.Sprintf("%s %d", m.Title, i)
  16. messages[i].Content = fmt.Sprintf("%s %d", m.Content, i)
  17. }
  18. return messages, nil
  19. }

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

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

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

  1. [{"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 部分,例如:

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

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

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

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

接口实例类型

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

设置接口实例:

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

获取接口实例:

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