Is it possible to implement an interface with unexported methods in another package? - go

I have written an interface for accounting system access. I would like to hide specific implementations of the interface from my program as I will only ever have one "active" accounting system. So I planned on making the methods of the interface unexported (hidden), and then having exported functions native to the base package that call a the same function from a local adapter.
package accounting
import "errors"
type IAdapter interface {
getInvoice() error
}
var adapter IAdapter
func SetAdapter(a IAdapter) {
adapter = a
}
func GetInvoice() error {
if (adapter == nil) {
return errors.New("No adapter set!")
}
return adapter.getInvoice()
}
__________________________________________________
package accountingsystem
type Adapter struct {}
func (a Adapter) getInvoice() error {return nil}
__________________________________________________
package main
import (
"accounting"
"accountingsystem"
)
function main() {
adapter := accountingsystem.Adapter{}
accounting.SetAdapter(adapter)
}
The problem with this is that the compiler complains, due to not being able to see the implementation of getInvoice() by accountingsystem.Adapter:
./main.go:2: cannot use adapter (type accountingsystem.Adapter) as type accounting.IAdapter in argument to accounting.SetAdapter:
accountingsystem.Adapter does not implement accounting.IAdapter (missing accounting.getInvoice method)
have accountingsystem.getInvoice() error
want accounting.getInvoice() error
Is there any way of being able to implement an interface with unexported methods in another package? Or am I thinking about this problem in a non-idiomatic way?

You can implement an interface with unexported methods using anonymous struct fields, but you cannot provide your own implementation of the unexported methods. For example, this version of Adapter satisfies the accounting.IAdapter interface.
type Adapter struct {
accounting.IAdapter
}
There's nothing that I can do with Adapter to provide my own implementation of the IAdapter.getInvoice() method.
This trick is not going to help you.
If you don't want other packages to use accountingsystem.Adapter directly, then make the type unexported and add a function for registering the adapter with the accounting package.
package accounting
type IAdapter interface {
GetInvoice() error
}
---
package accountingsystem
type adapter struct {}
func (a adapter) GetInvoice() error {return nil}
func SetupAdapter() {
accounting.SetAdapter(adapter{})
}
---
package main
func main() {
accountingsystem.SetupAdapter()
}

Related

In GoLang, how do you isolate packages that return more than a primitive value?

If I have a requests package that defines an interface TextExtractor with a GetText method that returns a Text type, the implementations must fulfill the TextExtractor contract exactly, and they are forced to import the Text type.
I have two possible implementations of TextExtractor - one that uses AWS Comprehend and one that uses AWS Textract.
aws_comprehend.go
package aws_comprehend
type AwsComprehend struct{}
func (a *AwsComprehend) GetText() *Text {
// do some stuff with aws comprehend...
return &Text{
Text: "text",
Language: "language",
}
}
type Text struct {
Text string
Language string
}
request.go
package requests
import "fmt"
type TextExtractor interface {
GetText() *Text
}
type Text struct {
Text string
Language string
}
func HandleRequest(textExtractor TextExtractor) {
text := textExtractor.GetText()
fmt.Println(text)
}
main.go
package main
import (
"aws_comprehend"
"requests"
)
func main() {
textExtractor := new(aws_comprehend.AwsComprehend)
requests.HandleRequest(textExtractor)
// this does not work:
// cannot use textExtractor (variable of type *aws_comprehend.AwsComprehend) as
// requests.TextExtractor value in argument to requests.HandleRequest:
// *aws_comprehend.AwsComprehend does not implement requests.TextExtractor
// (wrong type for method GetText)
// have GetText() *aws_comprehend.Text
// want GetText() *requests.Text
}
I understand why this doesn't work. It's because GoLang doesn't support Covariant Result Types. But my question is, what is the standard way to code this situation? The fact that GoLang provides implicit interfaces means that isolating packages is very easy: the calling package defines the interfaces that it uses, and it is passed implementations that fulfill those interfaces. This means that packages don't have to reference each other at all. But if a package defines an interface that returns anything more than a primitive value, then you have to deliberately share those value types. The code above would be fine if GetText returned a string. But the fact that it returns a struct or another interface, means the code can't be written this way.
I want the requests package not to know anything about the aws_comprehend package. This is because I have two implementations of the TextExtractor interface: One that uses AWS Comprehend, and one that uses AWS Textract. I also would prefer not to include a "intermediate" package that has interfaces that both the requests package and the aws_comprehend package inherit from. If both packages have to inherit the same interface, then it seems like it's just indirect coupling to me and it ruins the idea of a implicit interfaces.
I understand that GoLang is very opinionated - So what is the standard way to solve this problem?
first, your file layout is not valid. you cant have two files in the same
folder, with different packages. so below is a corrected layout. also I removed
all pointers, because they aren't needed for this example and are just distracting.
Finally, I updated the method to return the correct type, so that the code
actually compiles:
aws_comprehend/aws_comprehend.go:
package aws_comprehend
import "hello/requests"
type AwsComprehend struct{}
func (AwsComprehend) GetText() requests.Text {
return requests.Text{}
}
requests/request.go:
package requests
import "fmt"
type TextExtractor interface {
GetText() Text
}
type Text struct {
Language string
Text string
}
func HandleRequest(textExtractor TextExtractor) {
text := textExtractor.GetText()
fmt.Println(text)
}
main.go:
package main
import (
"hello/aws_comprehend"
"hello/requests"
)
func main() {
var textExtractor aws_comprehend.AwsComprehend
requests.HandleRequest(textExtractor)
}

Mock interface return type for dep injection

EDIT
As pointed out in the accepted answer, the issue here was doing go duck typing in the wrong direction. I'd like to add the following github issue as an attachment, since it provided me useful information in addition to #matt answer below:
https://github.com/golang/mock/issues/316#issuecomment-512043925
ORIGINAL POST
I'm new to dependencies injection, and wanted to test it on a module that uses couchbase go sdk. For this purpose I need interfaces to reproduce both Cluster and Bucket structures.
On the Cluster interface I need the Bucket() method, which has the following signature:
func (c *gocb.Cluster) Bucket(bucketName string) *gocb.Bucket
I also need the two following methods from the Bucket interface:
func (b *gocb.Bucket) Collection(collectionName string) gocb.*Collection
func (b *gocb.Bucket) DefaultCollection() *gocb.Collection
The tricky part is that both Cluster and Bucket methods have pointer receivers. This isn't hard in itself, since I know how to mock such methods alone (you just need to use a pointer to the type that implements the interface).
The issue is that one of the Cluster methods needs to return a pointer that implements the Bucket interface, since it also has pointer receivers methods. I tried many combinations, but each time I use an non-mocked *gocb.Cluster value as an argument to one of my functions, it fails because the Bucket method on the cluster instance isn't implemented correctly by the instance.
Below is my latest attempt:
package deps
import (
"github.com/couchbase/gocb/v2"
)
// Database mocks the gocb.Cluster interface.
type Database interface {
Bucket(bucketName string) *Bucket
}
// Bucket mocks the gocb.Bucket interface.
type Bucket interface {
Collection(collectionName string) *gocb.Collection
DefaultCollection() *gocb.Collection
}
The linter then returns the following error whenever I try to use an actual gocb.Cluster value:
I also tried to replace the Bucket method signature in my Database interface with:
// Database mocks the gocb.Cluster interface.
type Database interface {
Bucket(bucketName string) Bucket
}
Which again gives me the following lint error:
How can I implement an interface to mock both methods ?
I think the key concept that you're missing is that the mock object has to match the interface requirements of what you're mocking. That includes the parameters and return values of the methods.
type Database interface {
// Bucket(bucketName string) *Bucket // Wrong
Bucket(bucketName string) *gocb.Bucket // Correct
}
You can still use the return value of Database.Bucket as a deps.Bucket, given that you've also mocked that interface properly.
Unless I'm missing something about your testing process, this should do what you need.
package main
import (
"github.com/couchbase/gocb/v2"
)
// Database mocks the gocb.Cluster interface.
type Database interface {
Bucket(bucketName string) *gocb.Bucket
}
// Bucket mocks the gocb.Bucket interface.
type Bucket interface {
Collection(collectionName string) *gocb.Collection
DefaultCollection() *gocb.Collection
}
func someFunc(db Database) *gocb.Bucket {
return db.Bucket("")
}
func anotherFunc(bucket Bucket) {
bucket.Collection("")
}
func main() {
var cluster *gocb.Cluster
bucket := someFunc(cluster)
anotherFunc(bucket)
}

How to manage cyclic dependencies when have interface and its implementation in different packages

I have my project structure looks like this:
Structure of code:
hypervisor
├── hypervisor.go
├── hyperv
│   └── hyperv.go
└── virtualbox
├── vbox.go
└── vboxprops.go
Source code:
//hypervisor/hypervisor.go
package hypervisor
type Hypervisor interface {
Start(vmName string) error
ListMounts(vmName string) ([]MountPath, error)
//....
}
type MountPath struct {
HostPath string
GuestPath string
}
func detect() (Hypervisor, error) {
return &virtualbox.Virtualbox{}, nil // <<1 HERE
}
// ... other code
And have another (nested) package :
//hypervisor/virtualbox/vbox.go
package virtualbox
type Virtualbox struct {
}
func (*Virtualbox) Start(vmName string) error {
return vboxManage("startvm", vmName, "--type", "headless").Run()
}
func (*Virtualbox) ListMounts(vmName string) ([]hypervisor.MountPath, error) { // <<2 HERE
// ....
}
// ... other code
And as seen, of course, such code leads to import cycle not allowed . because of:
hypervisor pcakge referencing virtualbox.VirtualBox type
virtualbox package referencing hypervisor.MountPath type
I know if I move the struct MounthPath to another package would solve the issue, but I don't think is the correct solution design-wise.
Any suggestion?
One of easiest way I would do is to separate entities into entities package for example (in this case: the Hypervisor and Virtualbox struct are entities or whatever you want to call it).
This is most common design I think, so every struct that inner packages use will not cause cyclic deps.
Example of usage: all time package structs are on top package level. time.Time{}, time.Duration{}, etc. time.Duration does not sit on time/duration package.
Following suggestions from Dave Cheney to define interfaces by the caller will avoid cycle dependencies in most cases. But this will only solve flat data models. In your case, you have nested entities ie., HyperVisor has fucntion which returns MounthPath. We can model this in two ways
Define MouthPath in separate package (like you suggested). In
addition, defining the interface in the virtualbox package will help
in long term to provide alternative implementation for Hypervisor.
Let virtualbox define both Hypervisor and MounthPath as interface. One disadvantage is that the hypervisor implementing package use virtualbox.MouthPath interface to satisfy the interface when passed like below.
//hypervisor/hypervisor.go
package hypervisor
type Hypervisor struct{
someField []virtualbox.MountPath
}
type MountPath struct { // this can be used as virtualbox.MountPath
hostPath string
guestPath string
}
func (m *MountPath) HostPath() string { return m.hostPath }
func (m *MountPath) GuestPath() string { return m.guestPath }
func detect() (Hypervisor, error) {
return &virtualbox.Virtualbox{}, nil // <<1 HERE
}
And have another package (Need not be nested)
//hypervisor/virtualbox/vbox.go
package virtualbox
type Hypervisor interface {
Start(vmName string) error
ListMounts(vmName string) ([]MountPath, error)
//....
}
type MountPath interface {
HostPath() string
GuestPath() string
}
type Virtualbox struct {}
func (*Virtualbox) Start(vmName string) error {
return vboxManage("startvm", vmName, "--type", "headless").Run()
}
func (*Virtualbox) ListMounts(vmName string) ([]MountPath, error) { // <<2 HERE
// ....
}

How to use a type parameter in an interface method?

I am trying to write a sample program to try and implementing the a data structure using Go generics.
As part of this I want to define a iterator interface. I have the following code:
package collection
type Iterator interface {
ForEachRemaining(action func[T any](T) error) error
// other methods
}
It keeps giving me following error
function type cannot have type parameters
Moving the type parameter to the method also doesn't work:
type Iterator interface {
ForEachRemaining[T any](action func(T) error) error
// other methods
}
Gives error:
methods cannot have type parameters
Is there any way to define generic interface
As the error suggests, methods cannot have type parameters of their own as per the latest design. However, they can use the generics from the interface or struct they belong to.
What you need is to specify the type parameter on the interface type as follows:
type Iterator[T any] interface {
// ...
}
and then use the T as any other type parameter in methods within the interface body. For example:
package main
import "fmt"
type Iterator[T any] interface {
ForEachRemaining(action func(T) error) error
// other methods
}
func main() {
fmt.Println("This program compiles")
}
Try it on Go playground.

Implementing interface from different package golang

I'm having some issues trying to implement an interface, defined in a different package in golang. I have made a minimal recreation of the problem below
Interface:
package interfaces
type Interface interface {
do(param int) int
}
Implementation:
package implementations
type Implementation struct{}
func (implementation *Implementation) do(param int) int {
return param
}
Main.go:
package main
import (
"test/implementing-interface-in-different-package/implementations"
"test/implementing-interface-in-different-package/interfaces"
)
func main() {
var interfaceImpl interfaces.Interface
interfaceImpl = &implementations.Implementation{}
}
Error message:
test/implementing-interface-in-different-package
./main.go:10:16: cannot use implementations.Implementation literal (type
implementations.Implementation) as type interfaces.Interface in assignment:
implementations.Implementation does not implement interfaces.Interface (missing interfaces.do method)
have implementations.do(int) int
want interfaces.do(int) int
Is it possible to implement an interface from a different package?
Thanks!
The problem is that your do function is not exported from the implementations package because it starts with a lower-case letter. Thus from the point of view of the main package, the variable interfaceImpl does not implement the interface because it cannot see the do function.
Rename your interface function to upper-case Do to resolve the problem.

Resources