接口中的相同结构

I have two separated modules, let's call them

  1. FileSaver
  2. VideoFileProducer

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 interface
  • videoFileSaver.NewVideoFileProducer receives implementation of videoFileSaver.VideoFileSaver interface

In 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.


Working solution #1 (anonymous structure)

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).


Not working approach #1 (Fileable/VideoFileable interfaces)

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.


Not working approach #1 (interface{})

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)?


Working solution #2 (Fileable/VideoFileable + interface{})

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.


Working solution #3 (Fileable/VideoFileable + reflection)

https://play.golang.org/p/4UPEmNkG-E

Converting VideoFileable to Fileable


Working solution #4 (interface{} + reflection)

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())
}