接口

时间:2016-10-01 13:34:42

标签: go dependency-injection interface type-conversion

我有两个独立的模块,让我们称之为

  1. FileSaver
  2. VideoFileProducer
  3. 我想尽可能地将它们分开,它们不应该了解彼此的结构和界面。

    FileSaver模块具有以下接口和结构:https://play.golang.org/p/OHObk0EPlG VideoFileProducer模块具有以下接口和结构: https://play.golang.org/p/iBOOaSPLc0

    如你所见:

    • fileSaver.FilevideoFileProducer.VideoFile
    • 相同
    • fileSaver.FileSaver界面与videoFileProducer.VideoFileSaver界面相同(不同之处在于方法SaveFile收到不同的__but_identical结构:FileVideoFile
    • fileSaver.fileSaverImpl实现了fileSaver.FileSaver接口
    • videoFileSaver.NewVideoFileProducer接收videoFileSaver.VideoFileSaver interface
    • 的实施

    在主程序包中我想将FileSaver注入VideoFileProducer: https://play.golang.org/p/8B1iMCLLBE

    我当然收到错误(https://play.golang.org/p/Eqr2ylKiQ6):

      

    FileSaver没有实现VideoFileSaver(SaveFile方法的类型错误)

         

    有SaveFile(文件)

         

    想要SaveFile(VideoFile)

    这两个结构完全相同,我们可以轻松地将VideoFile转换为File和File to VideoFile,但无论如何它们都有不同的类型。

    简而言之,我想在Main包中创建FileSaver接口,将其转换为VideoFileSaver并将其发送到VideoFileProducer。

    或以某种方式使FileSaver接口适合用作VideoFileSaver接口。

    工作解决方案#1(匿名结构)

    https://play.golang.org/p/NJRUCCp2xS

    这个解决方案使用匿名结构,它可能太大而无法在很多地方将其声明为匿名(我只有一种方法,但在实际应用中可能有很多方法)。

    不工作方法#1(Fileable / VideoFileable接口)

    https://play.golang.org/p/0rP4nn7j-T

    我试图让结构(File,VideoFile)实现具有将其转换为指定匿名结构的方法的接口,但是没有成功。

    不工作方法#1(接口{})

    https://play.golang.org/p/8Q1KHhzl_g

    以前的方法,但使用接口{}。由于Interface Assertion(底层类型不同)而无法正常工作

    有没有办法将界面直接转换为其他类型而不是底层类型(不使用反射)?

    工作解决方案#2(Fileable / VideoFileable + interface {})

    https://play.golang.org/p/xacNEjFG7D

    我结合了最后两种方法并添加了使用开关来检查“底层接口是否与所需的相同”。所以现在我可以将VideoFileable用作Fileable。

    然而,这不是最好看的解决方案,我仍然在寻找一种尽可能少使用匿名结构和接口{}的解决方案。

    工作解决方案#3(Fileable / VideoFileable + reflection)

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

    将VideoFileable转换为Fileable

    工作解决方案#4(接口{} +反射)

    https://play.golang.org/p/AneYQVm2Gc

    VideoFile直接转换为文件

    我对上述任何解决方案都不满意,仍然在寻找更干净的东西。

2 个答案:

答案 0 :(得分:1)

删除VideoFile结构并在两个地方使用File结构。如果您不想要video - > file依赖关系,则必须将常见类型(FileFileSaver)提取到第三个包(这与你的匿名结构解决方案的原因相同 - 两个包都可以访问结构的"定义和#34;)

类型包

package types

type File struct {
    Name string
    Size int
}

type FileSaver interface {
    SaveFile(File)
}

文件包

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{}
}

视频包

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

主程序包

package main

import (
    "file"
    "video"
)

func main() {
    NewVideoFileProducer(file.NewFileSaver())
}

答案 1 :(得分:0)

我已经实施了一个解决方案,正如我认为的那样,满足了要求。

它只是Adapter模式的一个实现。在高级模块层次结构中,我们创建了新的Service,它实现了VideoFileSaver接口并调用了FileSaver接口。

VideoFileSaver - (VideoFile) - >适配器 - (文件) - > FileSaver

https://play.golang.org/p/fO_ML6veWt

此解决方案的优点:模块的耦合极低,如果我们在适配器中添加额外的逻辑,File结构甚至可能与VideoFile完全不同

此解决方案的缺点:高度复杂性

不建议您使用此解决方案。它只是满足了我的好奇心,但在实际项目中而不是它你应该(大多数)使用更简单的方法来做这些事情 - 比如在第三个模块中创建一个公共结构(在低级别的层次结构中)。正如weberc2所描述的那样。