I've got two functions that accept different pointers pointing to different structs, but the structs have the same underlying function.
func Save(db *sql.DB) error {
db.Prepare(query)
}
func TxSave(tx *sql.Tx) error {
tx.Prepare(query)
}
I don't want to have to make changes in both functions when I need to extend this function in the future. How do I adhere to DRYness in golang with this scenario?
Create an interface such as:
type SQLRunner interface{
Prepare(query string) (*sql.Stmt, error)
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
Query(query string, args ...interface{}) (*Rows, error)
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
// add as many method shared by both sql.Tx qnd sql.DB
// ...
}
And then create a single method taking that interface:
func Save(s SQLRunner) error {
s.Prepare()
}
In go interface implementation is implicit, so you just have to pass *sql.Tx or *sql.DB to the save function:
Save(tx)
Save(db)
Here a good blog post about interfaces in go:http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go
Wow, I think I'm in love with Go. I can actually just do this by creating my own interface.
type Saver interface {
Prepare(query string) (*sql.Stmt, error)
}
func Save(db *sql.DB) error {
return GenericSave(db)
}
func TxSave(tx *sql.Tx) error {
return GenericSave(tx)
}
func GenericSave(saver Saver) error {
stmt := saver.Prepare(query)
// Do rest with saver
}
Related
Let's say I want to decouple github.com/go-redis/redis/v8 from my app, for the usual reasons.
What I have until now is this:
import (
"context"
"github.com/go-redis/redis/v8"
)
type Pubsub interface {
Publish(ctx context.Context, topic string, message interface{}) *redis.IntCmd
Subscribe(ctx context.Context, topics ...string) *redis.PubSub
}
type PubsubClient struct {
Client Pubsub
}
func (psClient *PubsubClient) Publish(ctx context.Context, topic string,
message interface{}) error {
err := psClient.Client.Publish(ctx, topic, message).Err()
if err != nil {
return err
}
return nil
}
func (psClient *PubsubClient) Subscribe(ctx context.Context,
topics ...string) <-chan *redis.Message {
redisPubsub := psClient.Client.Subscribe(ctx, topics...)
return redisPubsub.Channel()
}
How do I avoid exposing *redis.Message to the users of PubsubClient?
I'm a newbie in Go, so I thought using the following approach:
type ThirdPartyType struct{}
func ThirdPartyFunc() ThirdPartyType{
return ThirdPartyType{}
}
type MyType ThirdPartyType
type MyAbstractStuff interface {
ThirdPartyFunc() ThirdPartyType
}
type MyConcreteStuff struct{
Client MyAbstractStuff
}
func (mcs *MyConcreteStuff) MyFunc() MyType {
tpt := mcs.Client.ThirdPartyFunc()
return MyType(tpt)
}
So I moved forward and did this:
type PubsubMsgChan <-chan *redis.Message
func (psClient *PubsubClient) Subscribe(ctx context.Context,
topics ...string) PubsubMsgChan {
redisPubsub := psClient.Client.Subscribe(ctx, topics...)
return PubsubMsgChan(redisPubsub.Channel())
}
But this doesn't seem right. If I was a user of this code, I would understand it better if there was a <-chan in my Subscribe() return type signature, so the user wouldn't have to dig in to find out that PubsubMsgChan is a read-only channel.
Considering redisPubsub.Channel() returns <-chan *redis.Message, how can I keep the <-chan in my method signature and at the same time hide *redis.Message?
For starters, is my approach to decoupling in Go actually valid/good/workable?
I have the func
func(context.Context, *domain.Scorecard) (*domain.Scorecard, error)
And i want pass this as a param that receive a interface
warehouse interface {
Get(context.Context, *domain.Scorecard) (*domain.Scorecard, error)
}
Ex:
warehouseMock :=
usecase.WithScenarioFinder(
func(player *domain.Player) (*domain.Scenario, error) {
return nil,nil
)
The traditional form is create a struct that have the method Get, but i have curiosity if exist the way to tell "hey, that is a simple func, is the same firm (with no name), accept it"
Functions do not implement interfaces with only a single method of that same signature. See this Go issue for a discussion on the topic.
Create an adaptor type to convert a function to an interface:
type WarehouseFunc func(context.Context, *domain.Scorecard) (*domain.Scorecard, error)
func (f WarehouseFunc) Get(c context.Context, d *domain.Scorecard) (*domain.Scorecard, error) {
return f(c, d)
}
Convert an anonymous function to an interface like this:
itf = WarehouseFunc(func(c context.Context, d *domain.Scorecard) (*domain.Scorecard, error) {
return nil, nil
})
The package 'gopkg.in/redis.v3' contains some code
type Client struct {
}
func (*client) Eval (string, []string, []string) *Cmd {
}
type Cmd struct {
}
func (*Cmd) Result () (interface{}, error) {
}
Which works successfully in the following way
func myFunc (cli *redis.Client) {
result, err := cli.Eval('my script').Result()
}
The problem is that sometimes the Redis cluster gets hammered, has a moment, and the interface returned as a result is nil.
This is reasonably easy to handle but I wish to put a test in place that will ensure that it is actually handled and no type assertion panic occurs.
Traditionally I would insert a mock Redis client into myFunc that can ultimately return nil.
type redisClient interface {
Eval(string, []string, []string) redisCmd
}
type redisCmd interface {
Result() (interface{}, error)
}
func myFunc (cli redisClient) {
result, err := cli.Eval('my script').Result()
}
The problem I am facing is the compiler doesn't recognise that redis.Client satisfies the interface redisClient because it doesn't recognise that the redis.Cmd returned from Eval satisfies redisCmd.
> cannot use client (type *redis.Client) as type redisClient in argument to myFunc:
> *redis.Client does not implement redisClient (wrong type for Eval method)
> have Eval(sting, []string, []string) *redis.Cmd
> want Eval(sting, []string, []string) redisCmd
The problem is that your interface does not match the redis client. If you change the interface to:
type redisClient interface {
Eval(string, []string, []string) *redis.Cmd
}
it will compile. That being said, it looks like you really want rediscmd, so you will need to make a wrapper around the redis client:
type wrapper struct{
c *redis.Client
}
func (w wrapper) Eval(x sting, y []string, z []string) redisCmd {
return w.c.Eval(x,y,z) // This assumes that *redis.Cmd implements rediscmd
}
I have written a simple package, which basically consists of a lot of getter functions. Each file in this package corresponds to a service, so for instance the product file, contains functions relating to the product service/db, order file to order service etc. Each function takes as parameters a db resource to the underlying db, and parameters for the sql, eg. productid, name, orderid. Each of the functions returns a struct (eg. Order, product) or an error:
// product.go
package lib
type Product struct {
ID int
Name string
Price float
}
func GetProductById(DB *sql.DB, ID int) (p Product, err error) {
q := "SELECT * FROM product WHERE id = " + ID
...
}
func GetProductByName(DB *sql.DB, name string) (p Product, err error) {
...
}
// order.go
package lib
type Order struct {
ID int
Date string
Items []items
}
func GetOrderById(DB *sql.DB, ID int) (o Order, err error) {
...
}
The problem is that I'm not able to mock these functions from my main package. What I really like to do, is to rewrite the package, so I somehow can pass to function to a type instead. But I'm not sure how to do this. Especially not when the functions take different input parameters and return different structs. Is there a way to do this?
In Go you can NOT mock a function, or a method declared on a concrete type.
For example:
func F() { ... }
func (T) M() { ... }
F and M are not mockable in Go.
However you can mock function values, whether they are variables, fields on a struct, or parameters passed to other functions.
For example:
var Fn = func() { ... }
type S struct {
Fn func()
}
func F(Fn func())
Fn in all three cases is mockable.
The other thing that you can mock in Go, and the prefered option most of the time, is an interface.
For example:
type ProductRepository interface {
GetProductById(DB *sql.DB, ID int) (p Product, err error)
}
// the real implementater of the interface
type ProductStore struct{}
func (ProductStore) GetProductById(DB *sql.DB, ID int) (p Product, err error) {
q := "SELECT * FROM product WHERE id = ?"
// ...
}
// the mock implementer
type ProductRepositoryMock struct {}
func (ProductRepositoryMock) GetProductById(DB *sql.DB, ID int) (p Product, err error) {
// ...
}
Now any piece of code that depends on ProductRepository can be passed a value of type ProductStore when you're in "normal mode" and a value of type ProductRepositoryMock when you're testing.
Another option using interfaces, which allows you to keep your functions mostly unchaged is to define an interface that mimics the methods of *sql.DB then use that interface type as the type to be passed to your functions, implement a mock version of that interface and use that during testing.
For example:
type DBIface interface {
Query(query string, args ...interface{}) (*sql.Rows, error)
// ...
// It's enough to implement only those methods that
// the functions that depend on DBIface actually use.
// If none of your functions ever calls SetConnMaxLifetime
// you don't need to declare that method on the DBIface type.
}
type DBMock struct {}
func (DBMock) Query(query string, args ...interface{}) (*sql.Rows, error) {
// ...
}
func GetProductByName(DB DBIface, name string) (p Product, err error) {
...
}
DB parameter to GetProductByName is now mockable.
I have an interface:
type Reader interface {
// Read IV and Master header
ReadMaster(p []byte, full bool) (int, error)
// Read User header
ReadUser(p []byte, full bool) (int, error)
// Read Content data
ReadContent(p []byte) (int, error)
}
And I have three structs are compatible with the interface. All the three structs have the samve method ReadUser. So I have to do:
func (r *s1) ReadUser(buf []byte, full bool) (int, error) {
//.... code 1 ....
}
func (r *s2) ReadUser(buf []byte, full bool) (int, error) {
//.... code 2 ....
}
func (r *s3) ReadUser(buf []byte, full bool) (int, error) {
//.... code 3 ....
}
However, the "code1", "code2" and "code3" above are exactly the same. It's there a good way to reduce the duplicate codes? E.g. define the function once and assign it to three struct?
Wrap it in its own type. Remembering too that interfaces in Go should only provide contracts for small specific tasks. It is very common for an interface to contain only a single method.
type UserReader interface {
ReadUser(p []byte, full bool) (int, error)
}
type UserRepo struct {
}
Add the method to that type:
func (ur *UserRepo) ReadUser(p []byte, full bool) (int, error) {
// code to read a user
}
Then, embed it in your other types:
type s1 struct {
*UserRepo
// other stuff here..
}
type s2 struct {
*UserRepo
// other stuff here..
}
type s3 struct {
*UserRepo
// other stuff here..
}
Then you can:
u := s1{}
i, err := u.ReadUser(..., ...)
u2 := s2{}
i2, err2 := u2.ReadUser(..., ...)
// etc..
..and you can also do:
doStuff(u)
doStuff(u2)
.. where doStuff is:
func doStuff(u UserReader) {
// any of the three structs
}
Click here to see it in the Playground