Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: Derive interface type from a concrete type #23886

Closed
guangda-hu opened this issue Feb 17, 2018 · 2 comments
Closed

proposal: Derive interface type from a concrete type #23886

guangda-hu opened this issue Feb 17, 2018 · 2 comments
Labels
FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Milestone

Comments

@guangda-hu
Copy link

I'm not sure if this is a good idea. But I have thought about it several times and would like to share it in case it has not been considered. This is for future versions of Go (probably Go 2.0).

Proposal

It would be nice if there is a way to define an interface type that is the method set of some arbitrary type T. My proposal is to simply generalize the existing interface embedding:

interface {
  T
}

Currently T can only be an interface. The proposal is to allow other types as well, and the interface contains the method sets of all embedded types.

  • The same restrictions of struct embedding applies: T must not be a pointer type, and for non-interface type, *T can also be embeded.
  • The rules of interface embedding also applies: the method sets of embedded types must be disjoint.

Application

Suppose I need to write the following function that uses a concrete type someservice.Client:

func MyFunc(*someservice.Client) {
 // ....
}

To write unit test for MyFunc, I want to fake someservice.Client. An easy way is to change the above code to

type myInterface interface {
  // all methods needed
}
var _ myInterface = &someservice.Client{}
func MyFunc(myInterface) {
}

Here myInterface contains a subset of the method set of *someservice.Client. However, if the method set is large, and if I need to change code back and forth (so it's hard to determine which methods are used), it would be tedious to keep myInterface up to date. The proposal will solve the problem:

type myInterface interface {
  *someservice.Client
}

Note 1

A google search shows that people have written tools to generate interface from struct: https://github.com/vburenin/ifacemaker (not necessarily to solve the same problem I mentioned though).

Note2

Another note is that I think this can somewhat make "polymorphism" easier in Go. Struct embedding is like "inherence" in many ways, but not always. Suppose we have

type Derived struct {
  Base
}

We still need an interface type that contains the methods of Base if we want something that can hold either a Base or a Derived. And the proposed interface { Base } is such an interface.

@gopherbot gopherbot added this to the Proposal milestone Feb 17, 2018
@davecheney
Copy link
Contributor

I think this proposal is symptomatic of a design flaw.

  1. Interfaces should have a small number of methods as they describe behaviour, not identity. If an interface has a large set of methods then that is a clear sign of breaking the single responsibility principal.
  2. An interface abstracts over behaviour, not types. It is common to see interfaces declared when there is only one implementation. Don’t do that.
  3. Callers should define interfaces to declare the behaviour they expect. These interfaces should be smaller than the method set of implementations passed through those interfaces. That helps the caller declare precisely the behaviour it expects.

In summary, I think changing the language to make it easier to automatically generate interface definitions where the member methods are too numerous or too unstable is solving the wrong problem.

@dsnet dsnet added LanguageChange v2 A language change or incompatible library change labels Feb 17, 2018
@ianlancetaylor
Copy link
Contributor

The only reason to have an interface type is to have multiple implementations. If the interface type is derived directly from one of the implementations, then any change to that implementation requires changing all the other implementations, or using them will fail. It seems a better methodology to start with the interface that does what you need, and write types that implement that. Or, in other words, the mechanism proposed here will encourage people to start with details of the implementation rather than the desired interface. It will also lead to unnecessary ties between types that are not related other than implementing the same interface.

@golang golang locked and limited conversation to collaborators Apr 24, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Projects
None yet
Development

No branches or pull requests

5 participants