2016-07-19 40 views
1

Ich habe Probleme, die "Go-way" zu finden, um eine Code-Duplizierung zu lösen. Hier ist das Problem. Beachten Sie Folgendes:Reduzierung der Code-Duplizierung in Golang

type (
    WithKey interface { 
    key() string 
    } 

    SharedFunctionality interface { 
    WithKey 
    MethodA() string 
    MethodB() string 
    // ... etc ... 
    } 

    FirstType struct { ... } 
    SecondType struct { ... } 
    // ... etc ... 
) 
func (ft *FirstType) key() string { ... } 
func (st *SecondType) key() string { ... } 

Nun werden die Methoden in SharedFunctionality sie hängen nur von den Ergebnissen der key() Methode. Ich konnte sie wie das Gerät folgende:

func runMethodA(k WithKey) string { 
    key := k.key() 
    // do something and return a string 
} 
func runMethodB(k WithKey) string { 
    key := k.key() 
    // do something else and return a string 
} 

func (ft *FirstType) MethodA() string { return runMethodA(ft) } 
func (ft *FirstType) MethodB() string { return runMethodB(ft) } 
func (st *SecondType) MethodA() string { return runMethodA(st) } 
func (st *SecondType) MethodB() string { return runMethodB(st) } 

Was ich über diesen Ansatz nicht mag, ist, dass, wie ich mehr Typen hinzufügen (ThirdType, FourthType, usw.) oder, wie ich mehr Methoden zu SharedFunctionality hinzufügen, ich habe Tonnen hinzufügen von Boilerplate-Code ... speziell für M-Methoden in SharedFunctionality und N-Typen würde ich M * N-Einzeiler wie die 4 oben buchstabieren müssen.

Was würde ich Liebe zu tun ist, so etwas wie:

func (k WithKey) MethodA() string { 
    key := k.key() 
    // do something 
} 

Mit anderen Worten: Ich würde gerne eine Methode einer Schnittstelle zu definieren. Bedeutung: Alle Objekte, die "WithKey" implementieren, erhalten automatisch MethodA() string, MethodB() string usw., daher würden sie automatisch die SharedFunctionality Schnittstelle implementieren. So etwas wie Standardmethoden in Java-Schnittstellen.

Aber ich weiß, es ist unmöglich, eine Methode in einer Schnittstelle zu definieren ...

Was ist der Go-Weg, um dieses Problem zu lösen?

Ich habe einen Ansatz gesehen, wo ich eine Struktur mit einem anonymen Feld des Interface-Typ schaffen würde, und dann werden die Methoden dort implementieren:

type SharedFuncStruct struct { 
    WithKey 
} 
func (sfs *SharedFuncStruct) MethodA() string { 
    key := sfs.key() 
    // whatever 
} 
// same for MethodB() 

Dann, es zu benutzen, ich möchte etwas tun:

first := ... getFirstTypeValue() 
sfs := &SharedFuncStruct{first} 
sfs.MethodA() // etc 

Das sieht so aus, als könnte es funktionieren, aber es fühlt sich immer noch nach zu viel Vorabcode an.

Andere Alternativen?

+0

Sie haben Ihre Optionen ziemlich genau beschrieben. Und siehe dave Antwort unten. Und denken Sie darüber nach: Was würde passieren, wenn ein konkreter Typ neben 'key()' bereits eine Methode 'MethodA()' hätte? Mindestens _embedding_ macht es unzweideutig, was 'MethodA()' bedeuten würde (in der _ "flachsten Tiefe" _). – icza

+0

Ihre Frage ist im Wesentlichen "wie mache ich Implementierungsvererbung und virtuelle Methoden in go", aber ich denke, Sie haben vielleicht ein xy-Problem. http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem. Wenn Sie einen kleinen Kontext von dem beschreiben, was Sie tun, gibt es wahrscheinlich eine Lösung, die nicht im objektorientierten Java-Design-Design verwurzelt ist, das besser in die Zukunft passt. –

Antwort

1

Fun Tatsache: Sie Schnittstellen in structs einbetten können, und die Struktur dann erfüllt automatisch die Schnittstelle. Sie können diese verwenden, um effektiv Methoden auf Schnittstellen zu definieren:

https://play.golang.org/p/ZufTOzr9ig

type (
    WithKey interface { 
     key() string 
    } 

    SharedFunctionality interface { 
     WithKey 
     MethodA() string 
     MethodB() string 
    } 

    KeyHolder struct { 
     WithKey 
    } 

    FirstType struct { ... } 
    SecondType struct { ... } 
) 

func (k *KeyHolder) MethodA() string { 
    key := k.key() 
    // ... 
} 

func (k *KeyHolder) MethodB() string { 
    key := k.key() 
    // ... 
} 

func NewSharedFunctionality(w WithKey) SharedFunctionality { 
    return &KeyHolder{w} 
} 

func (ft *FirstType) key() string { ... } 
func (st *SecondType) key() string { ... } 

In diesem Fall wird die KeyHolder struct die WithKey Schnittstelle einbettet und damit alles halten kann, der die key() string Methode hat (die beide FirstType und SecondType haben).Sie können dann MethodA und MethodB für diese Struktur definieren, und diese Struktur erfüllt dann sowohl die WithKey-Schnittstelle (weil sie eingebettet ist) als auch die SharedFunctionality-Schnittstelle, wobei der von der eingebetteten WithKey zurückgegebene Schlüssel verwendet wird.

Mit anderen Worten, statt FirstType in WithKey Einwickeln und dann in SharedFunctionality (was bedeutet, FirstType selbst key() zu definieren hat, MethodA() und MethodB()), wickeln Sie FirstType in WithKey, dann ist es einbetten (als WithKey-Schnittstelle) in einigen andere Struktur, die nur existiert, um diese Standardmethoden zu definieren MethodA und MethodB, die dann die SharedFunctionality Schnittstelle erfüllt.

+0

Ich mag diesen Ansatz. Es ist immer noch so, dass ich * more * tippen muss, als ich erwarten würde (zB 'NewSharedFunctionatily (firstTypeInstance)'), also kann ich '' MethodA() ') nennen, aber es scheint, als wäre es das Beste, was ich bekommen werde. Ich mag Daves Ansatz auch, aber zumindest scheint es, dass diese Lösung besser für mich funktioniert (dh, dass ein Objekt die Methoden implementiert, anstatt Funktionen auf einem Paket aufrufen zu müssen). Vielen Dank! –

2

Es sieht für mich so aus, als müssten Sie ein Paket extrahieren. Die Art, wie ich die Funktion hätte, ist

package keyed 

type hasKey interface { 
    Key() string 
} 

func MethodA(k hasKey) string { 
    key := k.Key() 
    // whatever 
} 

func MethodB(k hasKey) string { 
    key := k.Key() 
    // whatever 
} 

und dann

package your_package 

import "keyed" 

type (
    FirstType struct { ... } 
    SecondType struct { ... } 
) 

func (ft *FirstType) Key() string { ... } 
func (st *SecondType) Key() string { ... } 

func main() { 
    first := &FirstType{} 
    second := &SecondType{} 
    keyed.MethodA(first) 
    keyed.MethodA(second) 
    keyed.MethodB(first) 
    keyed.MethodB(second) 
} 
+0

danke! Es reduziert auf jeden Fall die Menge des Boilerplate-Codes. Ich werde sicherlich daran denken, Pakete für bestimmte Szenarien zu erstellen. Für das spezifische Szenario, mit dem ich gerade arbeite, scheint Kaedys Vorschlag ein wenig besser zu sein - ich werde seine Lösung akzeptieren; aber danke auch für deine Lösung, sehr elegant und nützlich. +1! –