用接口来简化我的Go函数的代码重复

huangapple go评论82阅读模式
英文:

DRY out my Go function with interfaces

问题

我有以下的函数:

func (r *Resource) Create(kind string, data io.ReadCloser) (err error) {
  decoder := json.NewDecoder(data)
  r.Kind = kind

  switch kind {
  case "user":
    var user User
    if err = decoder.Decode(&user); err != nil {
      panic(err)
    }

    if err = user.Save(r.Context); err != nil {
      panic(err)
    }

    r.Data = user
    break

  case "space":
    var space Space
    if err = decoder.Decode(&space); err != nil {
      panic(err)
    }

    if err = space.Save(r.Context); err != nil {
      panic(err)
    }

    r.Data = space
    break

  case "room":
    var room Room
    if err = decoder.Decode(&room); err != nil {
      panic(err)
    }

    if err = room.Save(r.Context); err != nil {
      panic(err)
    }

    r.Data = room
    break

  case "element":
    var element Element
    if err = decoder.Decode(&element); err != nil {
      panic(err)
    }

    if err = element.Save(r.Context); err != nil {
      panic(err)
    }

    r.Data = element
    break

  default:
    break
  }
  return
}

如你所见,switch语句中的每个case除了接收JSON数据的结构体类型不同外,其他部分都是相同的。

我怀疑接口和类型断言中有一个答案。

编辑:

我已经将保存部分拆分为一个单独的方法,但我仍然无法找到一种好的方法来将JSON对象解码为适当的结构体,而不使用switch语句。

func (r *Resource) Create(kind string, data io.ReadCloser) (err error) {
  decoder := json.NewDecoder(data)
  r.Kind = kind

  switch kind {
  case "user":
    var user User
    if err = decoder.Decode(&user); err != nil {
      panic(err)
    }
    r.saveEntity(&user)
    break

  case "space":
    var space Space
    if err = decoder.Decode(&space); err != nil {
      panic(err)
    }
    r.saveEntity(&space)
    break

  case "room":
    var room Room
    if err = decoder.Decode(&room); err != nil {
      panic(err)
    }
    r.saveEntity(&room)
    break

  case "element":
    var element Element
    if err = decoder.Decode(&element); err != nil {
      panic(err)
    }
    r.saveEntity(&element)
    break

  default:
    break
  }
  return
}

func (r *Resource) saveEntity(e Entity) {
  if err := e.Save(r.Context); err != nil {
    panic(err)
  }

  r.Data = e
}
英文:

I have the following function:

func (r *Resource) Create(kind string, data io.ReadCloser) (err error) {
  decoder := json.NewDecoder(data)
  r.Kind = kind

  switch kind {
  case "user":
    var user User
    if err = decoder.Decode(&user); err != nil {
      panic(err)
    }

    if err = user.Save(r.Context); err != nil {
      panic(err)
    }

    r.Data = user
    break

  case "space":
    var space Space
    if err = decoder.Decode(&space); err != nil {
      panic(err)
    }

    if err = space.Save(r.Context); err != nil {
      panic(err)
    }

    r.Data = space
    break

  case "room":
    var room Room
    if err = decoder.Decode(&room); err != nil {
      panic(err)
    }

    if err = room.Save(r.Context); err != nil {
      panic(err)
    }

    r.Data = room
    break

  case "element":
    var element Element
    if err = decoder.Decode(&element); err != nil {
      panic(err)
    }

    if err = element.Save(r.Context); err != nil {
      panic(err)
    }

    r.Data = element
    break

  default:
    break
  }
  return
}

As you can see, each case in the switch is identical except for the type of the struct that receives the JSON data.

I suspect that there's an answer in interfaces and type assertion.

EDIT:

I was able to break out the saving part into a separate method, but I still can't figure out a good way to decode the JSON object into the appropriate struct without the switch statement.

func (r *Resource) Create(kind string, data io.ReadCloser) (err error) {
  decoder := json.NewDecoder(data)
  r.Kind = kind

  switch kind {
  case "user":
    var user User
    if err = decoder.Decode(&user); err != nil {
      panic(err)
    }
    r.saveEntity(&user)
    break

  case "space":
    var space Space
    if err = decoder.Decode(&space); err != nil {
      panic(err)
    }
    r.saveEntity(&space)
    break

  case "room":
    var room Room
    if err = decoder.Decode(&room); err != nil {
      panic(err)
    }
    r.saveEntity(&room)
    break

  case "element":
    var element Element
    if err = decoder.Decode(&element); err != nil {
      panic(err)
    }
    r.saveEntity(&element)
    break

  default:
    break
  }
  return
}

func (r *Resource) saveEntity(e Entity) {
  if err := e.Save(r.Context); err != nil {
    panic(err)
  }

  r.Data = e
}

答案1

得分: 2

你可以将实例化移动到一行函数中,并创建一个映射,将种类映射到相应的实例化函数。代码的其余部分应该是可重用的。

示例:

kinds := map[string]func() Entity {
    "user": func() Entity { return &User{} },
    "space": func() Entity { return &Space{} },
    "room": func() Entity { return &Room{} },
}

func Create(kind string) {
    instance := kinds[kind]()

    decoder.Decode(instance)

    saveEntity(instance)
}
英文:

You could move the instantiation to one-line functions and create a mapping which maps the
kind to the respective instantiation function. The rest of the code should be re-usable.

Example:

kinds := map[string]func() Entity {
	"user": func() Entity { return &User{} },
	"space": func() Entity { return &Space{} },
	"room": func() Entity { return &Room{} },
}

func Create(kind string) {
	instance := kinds[kind]()

	decoder.Decode(instance)

	saveEntity(instance)
}

huangapple
  • 本文由 发表于 2013年7月7日 06:35:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/17507697.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定