oop - Go: Ensuring embedded structs implement interface without introducing ambiguity -


i'm trying clean code base doing better job defining interfaces , using embedded structs reuse functionality. in case have many entity types can linked various objects. want define interfaces capture requirements , structs implement interfaces can embedded entities.

// entities implement interface type entity interface {   identifier()   type() }  // interface entities can link foos type foolinker interface {   linkfoo() }  type foolinkerentity struct {   foo []*foo }  func (f *foolinkerentity) linkfoo() {   // issue: need access identifier() , type() here   // foolinkerentity doesn't implement entity }  // interface entities can link bars type barlinker interface {   linkbar() }  type barlinkerentity struct {   bar []*bar }  func (b *barlinkerentity) linkbar() {   // issues: need access identifier() , type() here   // barlinkerentity doesn't implement entity } 

so first thought have foolinkerentity , barlinkerentity implement entity interface.

// implementation of entity interface type entitymodel struct {     id string     object string }  func (e *entitymodel) identifier() { return e.id } func (e *entitymodel) type() { return e.type }  type foolinkerentity struct {   entitymodel   foo []*foo }  type barlinkerentity struct {   entitymodel   bar []*bar } 

however, ends ambiguity error types can link both foos , bars.

// baz.identifier() ambiguous between entitymodel, foolinkerentity, // , barlinkerentity. type baz struct {     entitymodel     foolinkerentity     barlinkerentity } 

what's correct go way structure type of code? type assertion in linkfoo() , linkbar() identifier() , type()? there way check @ compile time instead of runtime?

go not (quite) object oriented langauge: not have classes , does not have type inheritance; supports similar construct called embedding both on struct level , on interface level, , have methods.

so should stop thinking in oop , start thinking in composition. since said in comments foolinkerentity never used on own, helps achieve want in clean way.

i use new names , less functionality concentrate on problem , solution, results in shorter code , easier understand.

the full code can viewed , tested on go playground.

entity

the simple entity , implementation this:

type entity interface {     id() int }  type entityimpl struct{ id int }  func (e *entityimpl) id() int { return e.id } 

foo , bar

in example foolinkerentity , barlinkerentity decorators, don't need embed (extend in oop) entity, , implementations don't need embed entityimpl. however, since want use entity.id() method, need entity value, may or may not entityimpl, let's not restrict implementation. may choose embed or make "regular" struct field, doesn't matter (both works):

type foo interface {     sayfoo() }  type fooimpl struct {     entity }  func (f *fooimpl) sayfoo() { fmt.println("foo", f.id()) }  type bar interface {     saybar() }  type barimpl struct {     entity }  func (b *barimpl) saybar() { fmt.println("bar", b.id()) } 

using foo , bar:

f := fooimpl{&entityimpl{1}} f.sayfoo() b := barimpl{&entityimpl{2}} b.saybar() 

output:

foo 1 bar 2 

foobarentity

now let's see "real" entity entity (implements entity) , has both features provided foo , bar:

type foobarentity interface {     entity     foo     bar     sayfoobar() }  type foobarentityimpl struct {     *entityimpl     fooimpl     barimpl }  func (x *foobarentityimpl) sayfoobar() {     fmt.println("foobar", x.id(), x.fooimpl.id(), x.barimpl.id()) } 

using foobarentity:

e := &entityimpl{3} x := foobarentityimpl{e, fooimpl{e}, barimpl{e}} x.sayfoo() x.saybar() x.sayfoobar() 

output:

foo 3 bar 3 foobar 3 3 3 

foobarentity round #2

if foobarentityimpl not need know (does not use) internals of entity, foo , bar implementations (entityimpl, fooimpl , barimpl in our cases), may choose embed interfaces , not implementations (but in case can't call x.fooimpl.id() because foo not implement entity - implementation detail our initial statement don't need / use it):

type foobarentityimpl struct {     entity     foo     bar }  func (x *foobarentityimpl) sayfoobar() { fmt.println("foobar", x.id()) } 

its usage same:

e := &entityimpl{3} x := foobarentityimpl{e, &fooimpl{e}, &barimpl{e}} x.sayfoo() x.saybar() x.sayfoobar() 

its output:

foo 3 bar 3 foobar 3 

try variant on go playground.

foobarentity creation

note when creating foobarentityimpl, value of entity used in multiple composite literals. since created 1 entity (entityimpl) , used in places, there one id used in different implementation classes, "reference" passed each structs, not duplicate / copy. intended / required usage.

since foobarentityimpl creation non-trivial , error-prone, recommended create constructor-like function:

func newfoobarentity(id int) foobarentity {     e := &entityimpl{id}     return &foobarentityimpl{e, &fooimpl{e}, &barimpl{e}} } 

note factory function newfoobarentity() returns value of interface type , not implementation type (good practice followed).

it practice make implementation types un-exported, , export interfaces, implementation names entityimpl, fooimpl, barimpl, foobarentityimpl.


some related questions worth checking out

what idiomatic way in go create complex hierarchy of structs?

is possible call overridden method parent struct in golang?

can embedded struct method have knowledge of parent/child?

go embedded struct call child method instead parent method


Comments

Popular posts from this blog

Ansible - ERROR! the field 'hosts' is required but was not set -

customize file_field button ruby on rails -

SoapUI on windows 10 - high DPI/4K scaling issue -