douwo1862 2014-12-02 16:35
浏览 223
已采纳

通过Go中的接口解耦…接口实现器的一部分?

OK. I know this is a FAQ, and I think the answer is "give up, it doesn't work that way", but I just want to make sure I'm not missing something.

I am still wrapping my head around best practices and rules for use of interfaces. I have code in different packages that I'd prefer to keep decoupled, something like so (doesn't work, or I wouldn't be here):

package A

type Foo struct {}

func (f *Foo) Bars() ([]*Foo, error) {
    foos := make([]*Foo, 0)

    // some loop which appends a bunch of related *Foo to foos
    return foos, nil
}

package B

type Foolike interface {
    Bars() []Foolike
}

func DoSomething(f Foolike) error {
    // blah
}

With this, the compiler complains:

cannot use f (type *A.Foo) as type Foolike in argument to B.DoSomething:
*A.Foo does not implement Foolike (wrong type for Bars method)
    have Bars() ([]*A.Foo, error)
    want Bars() ([]Foolike, error)

Now, I grok that []Foolike is not an interface signature itself; it's the signature for a slice of Foolike interfaces. I think I also grok that the compiler treats []*A.Foo and []Foolike as different things because ... (mumble memory allocation, strict typing mumble).

My question is: Is there a correct way to do what I ultimately want, which is to let B.DoSomething() accept an *A.Foo without having to import A and use *A.Foo in B.DoSomething()'s function signature (or worse, in the interface definition)? I'm not hung up on trying to trick the compiler or get into crazy runtime tricks. I understand that I could probably change the implementation of Foo.Bars() to return []Foolike, but that seems stupid and wrong (Why should A have to know anything about B? That breaks the whole point of decoupling things!).

I guess another option is to remove Bars() as a requirement for implementing the interface and rely on other methods to enforce the requirement. That feels less than ideal, though (what if Bars() is the only exported method?). Edit: No, that won't work because then I can't use Bars() in DoSomething(), because it's not defined in the interface. Sigh.

If I'm just Doing It Wrong™, I'll accept that and figure something else out, but I hope I'm just not getting some aspect of how it's supposed to work.

  • 写回答

1条回答 默认 最新

  • dt4320279 2014-12-03 05:51
    关注

    As the error message says, you can't treat the []FooLike and []*Foo types interchangeably.

    For the a []*Foo slice, the backing array will look something like this in memory:

    | value1 | value2 | value3 | ... | valueN |
    

    Since we know the values are going to be of type *Foo, they can be stored sequentially in a straight forward manner. In contrast, each element in a []FooLike slice could be of a different type (provided they conform to FooLike). So the backing array would look more like:

    | type1 | value1 | type2 | value2 | type3 | value3 | ... | typeN | valueN |
    

    So it isn't possible to do a simple cast between the types: it would be necessary to create a new slice and copy over the values.

    So your underlying type will need to return a slice of the interface type for this to work.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 FOC simulink
  • ¥15 咨询一下有关于王者荣耀赢藏战绩
  • ¥50 MacOS 使用虚拟机安装k8s
  • ¥500 亚马逊 COOKIE我如何才能实现 登录一个亚马逊账户 下发新 COOKIE ..我使用下发新COOKIE 导入ADS 指纹浏览器登录,我把账户密码 修改过后,原来下发新COOKIE 不会失效的方式
  • ¥20 玩游戏gpu和cpu利用率特别低,玩游戏卡顿
  • ¥25 oracle中的正则匹配
  • ¥15 关于#vscode#的问题:把软件卸载不会再出现蓝屏
  • ¥15 vimplus出现的错误
  • ¥15 usb无线网卡转typec口
  • ¥30 怎么使用AVL fire ESE软件自带的优化模式来优化设计Soot和NOx?