2013-02-14 3 views
4

Étant donné une interface et deux (ou plus) mises en œuvre, je lutte pour changer facilement la mise en œuvre lors de l'extension de la fonctionnalité.Passer la mise en œuvre de l'interface sans frais généraux

Par exemple supposer qu'il existe iNumber d'interface qui prend en charge Inc et chaîne et deux implémentations NumberInt32 et NumberInt64 leur mise en œuvre évidente. Supposons que je veux implémenter un EvenCounter au-dessus de INumber. Le EvenCounter n'a qu'un IncTwice et doit appeler Inc deux fois. J'ai du mal à obtenir les types corrects sans utiliser une structure supplémentaire entourant l'INumber dans 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 

Voici où je lutte

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? 
    } 
} 

intégration Juste un struct fonctionne ...

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() 
} 

je pourrais vivre avec la nécessité de mettre en œuvre la délégation manuellement pour chaque méthode, mais évidemment, je voudrais compter sur iNumber et non la mise en œuvre spécifique (ce signifierait changer beaucoup d'endroits pour essayer une autre mise en œuvre, Ho wever, je voudrais éviter l'indirection supplémentaire et (plus probablement?) l'espace supplémentaire. Existe-t-il un moyen d'éviter la structure et de dire directement que EvenCounter est un IN (spécifique) avec des méthodes supplémentaires?

BTW l'exemple réel est un ensemble d'entiers et une carte d'entiers à des entiers avec des millions d'instances tous entrelacés (et non, juste carte [int] bool ne suffira pas - trop lent, bitset est intéressant à l'utilisation cas, etc) et tester différentes implémentations de l'ensemble et mapper facilement en changeant 2-3 lignes dans le code (idéalement juste le type et peut-être la création générique d'instances ou faire des copies)

Toute aide appréciée et J'espère que cela n'a pas été demandé encore ...

Répondre

4

Votre variante en utilisant plongement ne fait pas embed. Embedded fields are anonymous and Go then delegates automatically.

Cela simplifie votre exemple:

type EvenCounter3 struct { 
    INumber 
} 

func (this *EvenCounter3) IncTwice() { 
    this.Inc() // using this.n.Inc() twice makes it slower 
    this.Inc() 
} 

Notez que String() est déléguée automatiquement ("promu" dans Go parler).

Quant à appeler Inc() faisant deux fois plus lent, bien, qui est une limitation de l'utilisation d'interfaces. Le point d'une interface est de ne pas exposer l'implémentation, donc vous ne pouvez pas accéder à sa variable numérique interne.

+0

Merci: Ok, je lisais sur les interfaces et non sur la structure. Un délégué automatique serait bien. J'hériterais toutes les méthodes publiques de INumber cependant?Avec un type concret je pourrais les cacher? – pba

+0

Oui, c'est vrai. –

+0

Je ne voulais pas accéder à la variable numérique. Le problème avec this.n.Inc() et this.Inc() est l'indirection supplémentaire this.n - pas l'appel à la méthode actuelle. La syntaxe avec les champs incorporés anonymes semble correcte. Cependant, un test rapide et sale (incing dans une boucle) montre qu'il peut y avoir en dessous la même indirection que la même pénalité que pour le champ nommé. Je suppose que je dois vivre avec ça. Merci! – pba

0

Je ne suis pas sûr que je comprends bien ce que vous essayez d'atteindre. Peut-être quelque chose comme ça?

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)


Sortie

44 
+0

Merci: En gros, juste sans la structure additionnelle autour du 'num' dans la définition de' Event'. Mais comme le souligne l'autre réponse, il semble y avoir un délégué automatique si aucun nom n'est donné. – pba

Questions connexes