I am trying to figure out how to build a datastore abstraction in Go. I think I understand the basics of interfaces. However, the problem I have is all of the examples online only show you the most simple case, nothing beyond it.
What I am trying to do is figure out how and where to put the SQL code. I have tried to write the simplest bit of code that can illustrate what I am trying to do (yes, there is no error code, yes, the path structure is not idiomatic). I have a database with two tables. One to store Circles and one to store Squares. I have objects in Go to make these. My directory structure is:
project/main.go
project/test.db
project/shapes/shape.go
project/shapes/circle/circle.go
project/shapes/square/square.go
project/datastore/datastore.go
project/datastore/sqlite3/sqlite3.go
The only way I can think of how to make this work is to put the SQL INSERT and SELECT code inside of the actual shape files (circle.go and square.go). But that feels really wrong. It seems like it should be part of the datastore/sqlite3 package some how. I just do not see how to make that work. Here is what I have so far:
main.go
package main
import (
"fmt"
"project/datastore/sqlite3"
"project/shapes/circle"
"project/shapes/square"
)
func main() {
c := circle.New(4)
area := c.Area()
fmt.Println("Area: ", area)
s := square.New(12)
area2 := s.Area()
fmt.Println("Area: ", area2)
db := sqlite3.New("test.db")
db.Put(c)
db.Close()
}
shapes/shape.go
package shapes
type Shaper interface {
Area() float64
}
shapes/circle/circle.go
package circle
import (
"project/shapes"
)
type CircleType struct {
Radius float64
}
func New(radius float64) shapes.Shaper {
var c CircleType
c.Radius = radius
return &c
}
func (c *CircleType) Area() float64 {
area := 3.1415 * c.Radius * c.Radius
return area
}
shapes/square/square.go
package square
import (
"project/shapes"
)
type SquareType struct {
Side float64
}
func New(side float64) shapes.Shaper {
var s SquareType
s.Side = side
return &s
}
func (s *SquareType) Area() float64 {
area := s.Side * s.Side
return area
}
datastore/datastore.go
package datastore
import (
"project/shapes"
)
type ShapeStorer interface {
Put(shape shapes.Shaper)
Close()
}
datastore/sqlite3/sqlite3.go
package sqlite3
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
"log"
"project/datastore"
"project/shapes"
)
type Sqlite3DatastoreType struct {
DB *sql.DB
}
func New(filename string) datastore.ShapeStorer {
var ds Sqlite3DatastoreType
db, sqlerr := sql.Open("sqlite3", filename)
if sqlerr != nil {
log.Fatalln("Unable to open file due to error: ", sqlerr)
}
ds.DB = db
return &ds
}
func (ds *Sqlite3DatastoreType) Close() {
err := ds.DB.Close()
if err != nil {
log.Fatalln(err)
}
}
func (ds *Sqlite3DatastoreType) Put(shape shapes.Shaper) {
log.Println("Help")
// here you could either do a switch statement on the object type
// or you could do something like shape.Write(), if Write() was defined
// on the interface of shape/shapes.go Shaper interface and then
// implemented in the Square and Circle objects.
}
Since the database tables will be different for the Circle and Square objects, some how I need to have a method for each one. I can get this to work if I add a method to the circle package and square package to do the insert. But like I said above, that feels like I am doing it wrong.
Thanks much in advance.