![Flyweight](/images/patterns/cards/flyweight-mini.png?id=422ca8d2f90614dce810a8812c626698)
Flyweight en Go
Flyweight es un patrón de diseño estructural que permite a los programas soportar grandes cantidades de objetos manteniendo un bajo uso de memoria.
El patrón lo logra compartiendo partes del estado del objeto entre varios objetos. En otras palabras, el Flyweight ahorra memoria RAM guardando en caché la misma información utilizada por distintos objetos.
Ejemplo conceptual
En un juego de contraataque, el terrorista y el contraterrorista tienen distintos tipos de vestimenta. Por simplificar, asumamos que terrorista y contraterrorista tienen un tipo de vestimenta cada uno. El objeto vestimenta está integrado en el objeto jugador, como se ve a continuación.
A continuación se encuentra la estructura de un jugador. Podemos ver que el objeto vestimenta está integrado en la estructura del jugador:
type player struct {
dress dress
playerType string // Puede ser T o CT
lat int
long int
}
Digamos que hay 5 terroristas y 5 contraterroristas, de modo que hay un total de 10 jugadores. Hay dos opciones en lo que respecta a la vestimenta.
-
Cada uno de los 10 objetos jugador crea un objeto vestimenta diferente y lo integra. Se creará un total de 10 objetos de vestimenta.
-
Creamos dos objetos de vestimenta:
- Objeto único de vestimenta de terrorista: Lo compartirán 5 terroristas.
- Objeto único de vestimenta de contraterrorista: Lo compartirán 5 contraterroristas.
Como puedes ver en la solución 1, se crea un total de 10 objetos de vestimenta, mientras que en la solución 2 solo se crean 2 objetos de vestimenta. La segunda solución es la que empleamos en el patrón de diseño Flyweight. Los dos objetos de vestimenta que creamos se denominan objetos flyweight.
El patrón Flyweight extrae las partes comunes y crea objetos flyweight. Estos objetos flyweight (vestimenta) pueden ser compartidos por muchos objetos (jugador). Esto reduce drásticamente el número de objetos de vestimenta y lo bueno es que, aunque crees más jugadores, será suficiente con dos objetos de vestimenta.
En el patrón Flyweight almacenamos los objetos flyweight en el campo mapa. Cuando se crean los otros objetos que comparten los objetos flyweight, los objetos flyweight se extraen del mapa.
Veamos qué partes de este sistema serán estados intrínsecos y extrínsecos:
-
Estado intrínseco: Vestimenta en el estado intrínseco, ya que puede ser compartido por varios objetos de terrorista y contraterrorista.
-
Estado extrínseco: La ubicación y el arma del jugador son un estado extrínseco, ya que son diferentes para cada objeto.
dressFactory.go: Fábrica flyweight
package main
import "fmt"
const (
//TerroristDressType terrorist dress type
TerroristDressType = "tDress"
//CounterTerrroristDressType terrorist dress type
CounterTerrroristDressType = "ctDress"
)
var (
dressFactorySingleInstance = &DressFactory{
dressMap: make(map[string]Dress),
}
)
type DressFactory struct {
dressMap map[string]Dress
}
func (d *DressFactory) getDressByType(dressType string) (Dress, error) {
if d.dressMap[dressType] != nil {
return d.dressMap[dressType], nil
}
if dressType == TerroristDressType {
d.dressMap[dressType] = newTerroristDress()
return d.dressMap[dressType], nil
}
if dressType == CounterTerrroristDressType {
d.dressMap[dressType] = newCounterTerroristDress()
return d.dressMap[dressType], nil
}
return nil, fmt.Errorf("Wrong dress type passed")
}
func getDressFactorySingleInstance() *DressFactory {
return dressFactorySingleInstance
}
dress.go: Interfaz flyweight
package main
type Dress interface {
getColor() string
}
terroristDress.go: Objeto concreto flyweight
package main
type TerroristDress struct {
color string
}
func (t *TerroristDress) getColor() string {
return t.color
}
func newTerroristDress() *TerroristDress {
return &TerroristDress{color: "red"}
}
counterTerroristDress.go: Objeto concreto flyweight
package main
type CounterTerroristDress struct {
color string
}
func (c *CounterTerroristDress) getColor() string {
return c.color
}
func newCounterTerroristDress() *CounterTerroristDress {
return &CounterTerroristDress{color: "green"}
}
player.go: Contexto
package main
type Player struct {
dress Dress
playerType string
lat int
long int
}
func newPlayer(playerType, dressType string) *Player {
dress, _ := getDressFactorySingleInstance().getDressByType(dressType)
return &Player{
playerType: playerType,
dress: dress,
}
}
func (p *Player) newLocation(lat, long int) {
p.lat = lat
p.long = long
}
game.go
package main
type game struct {
terrorists []*Player
counterTerrorists []*Player
}
func newGame() *game {
return &game{
terrorists: make([]*Player, 1),
counterTerrorists: make([]*Player, 1),
}
}
func (c *game) addTerrorist(dressType string) {
player := newPlayer("T", dressType)
c.terrorists = append(c.terrorists, player)
return
}
func (c *game) addCounterTerrorist(dressType string) {
player := newPlayer("CT", dressType)
c.counterTerrorists = append(c.counterTerrorists, player)
return
}
main.go: Código cliente
package main
import "fmt"
func main() {
game := newGame()
//Add Terrorist
game.addTerrorist(TerroristDressType)
game.addTerrorist(TerroristDressType)
game.addTerrorist(TerroristDressType)
game.addTerrorist(TerroristDressType)
//Add CounterTerrorist
game.addCounterTerrorist(CounterTerrroristDressType)
game.addCounterTerrorist(CounterTerrroristDressType)
game.addCounterTerrorist(CounterTerrroristDressType)
dressFactoryInstance := getDressFactorySingleInstance()
for dressType, dress := range dressFactoryInstance.dressMap {
fmt.Printf("DressColorType: %s\nDressColor: %s\n", dressType, dress.getColor())
}
}
output.txt: Resultado de la ejecución
DressColorType: ctDress
DressColor: green
DressColorType: tDress
DressColor: red