Help with understanding this Struct/Inheritance problem in golang?

blov · 2017-02-28 05:00:10 · 584 次点击    
这是一个分享于 2017-02-28 05:00:10 的资源,其中的信息可能已经有所发展或是发生改变。

Hello all, I am using Go to do homework in a database class rather than my usual Python as a way to learn Go. So I've learned how to scan data from a query directly into a struct using sqlx, but I have a question on the structs themselves. The example database has these tables: Pets, Customers, Visits, Owner History. (It's a vet if you havn't guessed) So I have made a struct for each table so I can pull the data from the query and so on. The goal of the program is to give a simple UI to get data from the database, so for example, you could choose "Select all data in Pets table" from a menu and it executes that query and prints the results. Am I going to have to write a different function for each table for every different scenario, or could I have a single selectAll function that takes a table's name as an argument that can use a different type of struct depending on the table that is being queried? My first thought was to use some sort of inheritance and have a generic Data struct and have all of the others inherit from it, that way I could do something like:

var d Data
if table == "Pets"{
    d = Pet{}
}

Is something like that Possible or am I going about this the wrong way? The actual homework of setting up the UI does not confuse me, I just know that there is a better way than writing the same functions for each data type over and over again.


评论:

lobster_johnson:

That's not possible. Go doesn't have inheritance, and polymorphism is limited to interfaces. Go also doesn't have algebraic data types.

In cases like this, where you have "pure data" structs with little in the way of actual behaviour associated with them, you can sort of cheat with interfaces. For example:

type Record interface{
  isRecord()
}
type Pet struct {
  Name string
  Weight float64
}
func (Pet) isRecord() {}
type Customer struct {
  Name string
}
func (Customer) isRecord() {}

The isRecord is really just a dummy method so you can get some type safety at runtime; only Pet and Customer will be compatible with a variable or argument of type Record.

Now you can have:

func FetchAllRecords() []Record

...which can return a mix of pets and customers if it wants to. You need a type switch to identify what you get back:

for _, record := range FetchAllRecords() {
  switch r := record.(type) {
    case Pet:
      // is a Pet
    case Customer
      // is a Customer
  }
}

It's not ideal, but it does the job.

Another method is to not do this, but instead have things like:

func FetchAllPets() []Pet
func FetchAllCustomers() []Customer
// etc.

Of course, it's tedious to write the serialization/deserialization code for every possible table, and a common way to avoid such boilerplate is to generate the code.

Instead of reinventing the wheel, you should take a look at existing ORMs. Gorm is supposed to be good.


入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

584 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传