Potresti voler estrarre le funzionalità comuni di quei metodi di interfaccia per separare classi / funzioni e implementare solo gestori per la logica specifica dei componenti.
Un esempio leggermente semplificato:
type Component interface {
OnRender(ctx RenderContext)
GetComponents() []Component
AddComponent(Component)
}
Approccio n. 0: metodi di ereditarietà implementati uguali ovunque utilizzando i campi anonimi
type BaseComponent struct {
components []Component
}
func(c BaseComponent) GetComponents() []Component {
return c.components
}
func(c *BaseComponent) AddComponent(a Component) {
c.components = append(c.components, a)
}
Metodo 1: estrai la logica comune a una funzione libera
func Render(c Component, ctx RenderContext) {
// implement common render logic here
// call component specific handler
c.OnRender(ctx)
// update children
for _, child := range c.GetComponents() {
Render(child, ctx)
}
}
Approccio n. 2: estrai la logica comune a una "classe" dedicata (struct)
type ComponentRenderer struct {
// store common information required for rendering
}
func(cr *ComponentRenderer) Render(c Component, ctx RenderContext) {
// implement common render logic here
// call component specific handler
c.OnRender(ctx)
// update children
for _, child := range c.GetComponents() {
cr.Render(child, ctx)
}
}
Esempio per un componente finale:
type Button struct {
BaseComponent
// button specific fields
}
func(b *Button) OnRender(ctx RenderContext) {
// implement button specific rendering code here
}
Addendum for Approach # 2: Se vuoi, puoi dichiarare ComponentRenderer
come interfaccia e implementare specifici renderer per piattaforme diverse (es. uno per Windows, uno per Linux, uno per una pagina web, uno per una GUI in gioco , ...).
Puoi combinare questi diversi approcci per scopi diversi.
Nota: potresti provare a implementare un metodo Render
su BaseComponent
, ma tieni presente che non esiste alcun dispatch virtuale e nessun metodo che sovrascrive le strutture in Go (quindi il metodo Render
deve in qualche modo ottenere le informazioni su quale handler chiamare da qualche altra parte). Quindi consiglierei l'approccio # 0 solo nei casi in cui l'implementazione ogni utilizza la stessa logica.