I have two separated modules, let's call them
I want to separate them as much as possible, they shouldn't know about each others' structures and interfaces.
FileSaver module has these interfaces and structs: https://play.golang.org/p/OHObk0EPlG VideoFileProducer module has these interfaces and structs: https://play.golang.org/p/iBOOaSPLc0
As you can see:
fileSaver.File
is identical to the videoFileProducer.VideoFile
fileSaver.FileSaver
interface is identical to the videoFileProducer.VideoFileSaver
interface (the difference is that the method SaveFile
receives different_but_identical structures: File
and VideoFile
)fileSaver.fileSaverImpl
implements the fileSaver.FileSaver
interfacevideoFileSaver.NewVideoFileProducer
receives implementation of videoFileSaver.VideoFileSaver
interfaceIn Main package I want to Inject FileSaver into the VideoFileProducer: https://play.golang.org/p/8B1iMCLLBE
Of course I'm getting an error (https://play.golang.org/p/Eqr2ylKiQ6):
FileSaver does not implement VideoFileSaver (wrong type for SaveFile method)
have SaveFile(File)
want SaveFile(VideoFile)
These two structures are identical, we can easily convert VideoFile to File and File to VideoFile, but anyway they have different types.
In short I want to create FileSaver interface in Main package, cast it to VideoFileSaver and send it to the VideoFileProducer.
Or somehow to adapt FileSaver interface to be used as VideoFileSaver interface.
https://play.golang.org/p/NJRUCCp2xS
This solution uses anonymous structure and it can be too big to declare it as anonymous in so many places (I've got only one method but there can be many of them in real application).
https://play.golang.org/p/0rP4nn7j-T
I've tried to make the structure (File,VideoFile) to implement the interface that has a method for conversion it to the specified anonymous structure, but haven't succeeded.
https://play.golang.org/p/8Q1KHhzl_g
Previous approach but with using interface{}. Not working because of Interface Assertion (underlying types are different)
Is there any way to convert interface directly to other type than the underlying one (without using reflection)?
https://play.golang.org/p/xacNEjFG7D
I've combined last two approaches and added the using of switch to check "is underlying interface identical to the required one". So now I can to use VideoFileable as Fileable.
However it's not the best-looking solution and I'm still looking for a solution which uses as less anonymous structures and interface{} as possible.
https://play.golang.org/p/4UPEmNkG-E
Converting VideoFileable to Fileable
https://play.golang.org/p/AneYQVm2Gc
VideoFile converts directly to File
I'm not satisfied with any of the solutions above and still looking for something more clean.
I have implemented a solution that, as I think, satisfies requirements.
It's just an implementation of Adapter pattern. On high level of module hierarchy we create new Service that implements the VideoFileSaver interfaces and calls the FileSaver interface in it.
VideoFileSaver --(VideoFile)--> Adapter --(File)--> FileSaver
https://play.golang.org/p/fO_ML6veWt
Pros of this solution: Extremely low coupling of modules, File struct can even be absolutely different from VideoFile if we add additional logic in adapter
Cons of this solution: High complexity
I wouldn't recommend you to use this solution. It just has satisfied my curiosity but in real project instead of it you should (mostly) use the more simple way to do such things - like creating a common structure in the third module (on the low level of hierarchy). As has been described by weberc2.
Drop the VideoFile
struct and use the File
struct in both places. If you don't want a video
->file
dependency, you'll have to extract the common types (File
and FileSaver
) into a third package (this works for the same reason as your anonymous struct solution--the "definition" of the struct is accessible to both packages).
types package
package types
type File struct {
Name string
Size int
}
type FileSaver interface {
SaveFile(File)
}
file package
package file
import (
"fmt"
"types"
)
type fileSaverImpl struct{}
func (fs fileSaverImpl) SaveFile(f types.File) {
fmt.Println(f.Name, f.Size)
}
func NewFileSaver() types.FileSaver {
return fileSaverImpl{}
}
video package
package video
import "types"
type VideoFileProducer struct{}
// NOTE: This function title is misleading; it doesn't actually
// create a `VideoFileProducer`, but instead it makes a new file
// and saves it with the provisioned `FileSaver`.
func NewVideoFileProducer(fileSaver types.FileSaver) {
fileSaver.SaveFile(types.File{Name: "Video", Size: 256})
}
main package
package main
import (
"file"
"video"
)
func main() {
NewVideoFileProducer(file.NewFileSaver())
}