Go: proposal: Derive interface type from a concrete type

Created on 17 Feb 2018  路  2Comments  路  Source: golang/go

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.

FrozenDueToAge Go2 LanguageChange Proposal

Most helpful comment

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鈥檛 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.

All 2 comments

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鈥檛 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.

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.

Was this page helpful?
0 / 5 - 0 ratings