Η λύση του Max Malysh θα λειτουργούσε σε ορισμένες περιπτώσεις εάν δεν χρειάζεστε εργοστάσιο. Ωστόσο, η λύση που δόθηκε από τον Adrian Witas θα μπορούσε να προκαλέσει προβλήματα κυκλικών εξαρτήσεων.
Αυτός είναι ο τρόπος με τον οποίο πέτυχα την εφαρμογή μιας αφηρημένης τάξης με τον εύκολο τρόπο σεβόμενος τις κυκλικές εξαρτήσεις και τα καλά εργοστασιακά μοτίβα.
Ας υποθέσουμε ότι έχουμε την ακόλουθη δομή πακέτου για το στοιχείο μας
component
base
types.go
abstract.go
impl1
impl.go
impl2
impl.go
types.go
factory.go
Ορίστε τον ορισμό του στοιχείου, σε αυτό το παράδειγμα θα οριστεί εδώ:
component/types.go
package component
type IComponent interface{
B() int
A() int
Sum() int
Average() int
}
Τώρα ας υποθέσουμε ότι θέλουμε να δημιουργήσουμε μια αφηρημένη κλάση που να υλοποιεί μόνο το Sum και το Average, αλλά σε αυτήν την αφηρημένη υλοποίηση θα θέλαμε να έχουμε πρόσβαση για να χρησιμοποιήσουμε τις τιμές που επιστρέφονται από το υλοποιημένο A και B
Για να το πετύχουμε αυτό, θα πρέπει να ορίσουμε μια άλλη διεπαφή για τα αφηρημένα μέλη της αφηρημένης υλοποίησης
component/base/types.go
package base
type IAbstractComponentMembers {
A() int
B() int
}
Και τότε μπορούμε να προχωρήσουμε στην υλοποίηση της αφηρημένης «τάξης»
component/base/abstract.go
package base
type AbstractComponent struct {
IAbstractComponentsMember
}
func (a *AbstractComponent) Sum() int {
return a.A() + a.B()
}
func (a *AbstractComponent) Average() int {
return a.Sum() / 2
}
Και τώρα προχωράμε στις υλοποιήσεις
component/impl1/impl.go // Υποθέστε κάτι παρόμοιο για το impl2
package impl1
type ComponentImpl1 struct {
base.AbstractComponent
}
func (c *ComponentImpl1) A() int {
return 2
}
func (c *ComponentImpl1) A() int {
return 4
}
// Here is how we would build this component
func New() *ComponentImpl1 {
impl1 := &ComponentImpl1{}
abs:=&base.AbstractComponent{
IAbstractComponentsMember: impl1,
}
impl1.AbstractComponent = abs
return impl1
}
Ο λόγος που χρησιμοποιούμε μια ξεχωριστή διεπαφή για αυτό αντί να χρησιμοποιήσουμε το παράδειγμα Adrian Witas, είναι επειδή εάν χρησιμοποιήσουμε την ίδια διεπαφή σε αυτήν την περίπτωση, εάν εισαγάγουμε το πακέτο base στο strong>impl* για να χρησιμοποιήσουμε την αφηρημένη "κλάση" και επίσης εισάγουμε τα πακέτα impl* στο πακέτο components, ώστε το εργοστάσιο να τα καταχωρήσει, Θα βρείτε μια κυκλική αναφορά.
Έτσι θα μπορούσαμε να έχουμε μια εργοστασιακή υλοποίηση όπως αυτή
component/factory.go
package component
// Default component implementation to use
const defaultName = "impl1"
var instance *Factory
type Factory struct {
// Map of constructors for the components
ctors map[string]func() IComponent
}
func (f *factory) New() IComponent {
ret, _ := f.Create(defaultName)
return ret
}
func (f *factory) Create(name string) (IComponent, error) {
ctor, ok := f.ctors[name]
if !ok {
return nil, errors.New("component not found")
}
return ctor(), nil
}
func (f *factory) Register(name string, constructor func() IComponent) {
f.ctors[name] = constructor
}
func Factory() *Factory {
if instance == nil {
instance = &factory{ctors: map[string]func() IComponent{}}
}
return instance
}
// Here we register the implementations in the factory
func init() {
Factory().Register("impl1", func() IComponent { return impl1.New() })
Factory().Register("impl2", func() IComponent { return impl2.New() })
}
29.04.2018