Given an interface and two (or more) implementations I struggle to easily switch the implementation when extending the functionality.
For example assume that there is interface INumber which supports Inc and String and two implementations NumberInt32 and NumberInt64 with their obvious implementation. Assume that I want to implement an EvenCounter on top of INumber. The EvenCounter only has a IncTwice and shall call Inc twice. I struggle to get the types right without using an extra struct surrounding the INumber in EvenCounter.
type INumber interface {
Inc()
String() string
}
type NumberInt32 struct {
number int32
}
func NewNumberInt32() INumber {
ret := new(NumberInt32)
ret.number = 0
return ret
}
func (this *NumberInt32) Inc() {
this.number += 1
}
func (this *NumberInt32) String() string {
return fmt.Sprintf("%d", this.number)
}
// type NumberInt64.... // obvious
Here is where I struggle
type EvenCounter1 INumber // nope, additional methods not possible
type EvenCounter2 NumberInt32 // nope
func (this *EvenCounter2) IncTwice() {
for i:=0; i < 2; i+=1 {
// this.Inc() // Inc not found
// INumber(*this).Inc() // cannot convert
// in, ok := *this.(INumber) // cannot convert
// v, ok := this.(INumber) // cannot convert
// a concrete conversion a) does not work and b) won't help
// here it should be generic
// v, ok := this.(NumberInt32)
// How do I call Inc here on this?
}
}
Just embedding in a struct works...
type EvenCounter3 struct {
n INumber
}
func (this *EvenCounter3) IncTwice() {
n := this.n // that is a step I want to avoid
n.Inc() // using this.n.Inc() twice makes it slower
n.Inc()
}
func (this *EvenCounter3) String() string {
return this.n.String()
}
I could live with the need to implement delegation manually for each method, but obviously I would want to rely on INumber and not the specific implementation (that would mean changing lots of places to try another implementation, However, I would want to avoid the extra indirection and (most likely?) extra space. Is there a way to avoid the struct and directly say EvenCounter is a (specific) INumber with additional methods?
BTW the real example is a set of integers and a map of integers to integers with millions of instances all intertwined (and no, just map[int]bool won't suffice - too slow, bitset is interesting depening on use case, etc.) and testing different implementations of the set and map easily by changing 2-3 lines in the code (ideally just the type and maybe the generic creation of instances resp. making copies)
Any help appreciated and I hope this hasn't been asked yet...
Your variant using embedding does not actually embed. Embedded fields are anonymous and Go then delegates automatically.
This simplifies your example to:
type EvenCounter3 struct {
INumber
}
func (this *EvenCounter3) IncTwice() {
this.Inc() // using this.n.Inc() twice makes it slower
this.Inc()
}
Note that String() is automatically delegated ("promoted" in Go speak).
As to calling Inc() twice making it slower, well, that's a limitation of using interfaces. The point of an interface is to not expose the implementation, so you cannot access its internal number variable.
I'm not sure I understood correctly what you're trying to achieve. Perhaps something like this?
package main
import "fmt"
type Num interface {
Inc()
String() string
}
type Int32 struct {
int32
}
func (n *Int32) Inc() {
(*n).int32++
}
func (n Int32) String() string {
return fmt.Sprintf("%d", n.int32)
}
type EventCounter interface {
Num
IncTwice()
}
type Event struct {
Num
}
func (e Event) IncTwice() {
e.Inc()
e.Inc()
}
func main() {
e := Event{&Int32{42}}
e.IncTwice()
fmt.Println(e)
}
(Alse here)
Output
44