¿Alguien tiene un ejemplo específico de uso del patrón Flyweight? [cerrado]

21

He estado estudiando patrones de diseño y me encontré con el patrón de peso de la mosca. He estado tratando de ver oportunidades para usar el patrón en mis aplicaciones, pero tengo problemas para ver cómo usarlo. Además, ¿cuáles son algunas señales de que se está utilizando un patrón de peso de mosca cuando leo el código de otras personas?

Según la definición dice:

Use el uso compartido para admitir grandes cantidades de objetos de grano fino de manera eficiente.

Si lo leí bien, los diccionarios y las tablas hash podrían ser instancias de pesas de vuelo, ¿es correcto?

Gracias por adelantado.

Jeremy E
fuente
77
Solo una pequeña anécdota sobre flyweights: una vez tuve que crear grandes archivos de Excel (hasta 500k registros, más de 100 columnas) con una API de terceros. Los estilos para las células se volvieron extremadamente intensivos en memoria. Entonces, cada vez que se necesitaba un estilo, se verificaba una tabla hash si ya existía un estilo igual y luego se proporcionaba solo una referencia a este estilo. Esta modificación hizo posible esta exportación. Ahora, tener esa cantidad de datos en Excel es una locura en mi opinión. Pero los controladores tenían sus macros de análisis que querían conservar.
Falcon
99
Comentario: ¡Espero que las personas que escriben libros y artículos sobre patrones y OO vengan al mundo real del programador promedio y dejen de usar el inglés estilo abogado!
NoChan
1
"Una vez tuve que crear grandes archivos de Excel (hasta 500k registros, más de 100 columnas)" - eso no es mucho en comparación con lo que algunos comerciantes son capaces de crear ;-)
quant_dev
Después de leer varios de estos ejemplos, pensaría que la compresión de datos en memoria sería un excelente lugar para implementar esta técnica. ¡Gracias por la ayuda!
Jeremy E
Las celdas de tabla en GWT son pesos mosca.
usuario16764

Respuestas:

19

Un ejemplo está en las bibliotecas de Java. Java tiene tipos primitivos (p int. Ej. , Que es un entero de 32 bits) y envoltorios para ellos (p Integer. Ej. , Que se ajusta int). Hay métodos para "encajonar" un inten un Integery unbox un Integeren un int. Los envoltorios son necesarios porque los tipos primitivos no son objetos y, por lo tanto, no se pueden usar, por ejemplo, como claves en Maps ni se pueden colocar en Collections.

El método de boxeo utiliza una matriz de objetos de peso mosca como una especie de caché para Integers correspondientes a intvalores entre -128 y 127. Dado que esos son los valores que más probablemente se usarán como claves o se colocarán en colecciones, reduce la asignación y el uso de memoria. (Si hay 5000000 Integers que representan el valor 0 flotando, eso usa 5000000 veces más memoria que reutilizando la instancia de peso mosca).

Peter Taylor
fuente
1
Entonces, ¿el grupo interno de cadenas en C # es otro ejemplo del patrón de peso mosca correcto?
Jeremy E
1
@Jeremy E: Sí, en mi opinión, puede llamar a la cadena interna una aplicación del patrón de peso mosca, aunque para cadenas, no solo se trata del consumo de memoria, sino también de la eficiencia del tiempo de ejecución.
Falcon
Los punteros etiquetados con Objective-C llevan esto al extremo. Los enteros en caja de hasta 56 bits, y muchas cadenas de hasta seis caracteres, ni siquiera se asignan como objetos, pero toda la información se empaqueta en el puntero del objeto.
gnasher729
9

Gráficos. Por lo general, una imagen de trama (que es la columna vertebral de la mayoría de los gráficos de computadora de nivel de consumidor) es barata para la CPU, pero costosa para la memoria de trabajo (lo cual está bien porque la memoria es barata pero la CPU es costosa). Si esa imagen ráster se repetirá muchas veces al representar una interfaz de usuario más grande (desde iconos en una aplicación GUI de Windows hasta caracteres de una fuente en un procesador de texto, texturas en superficies en un juego 3D), tiene mucho sentido cargue la imagen en la memoria una vez y simplemente apúntela usando objetos muy simples que sean baratos de hacer y que, por sí mismos, no consuman mucha memoria. Un sprite, que es simplemente un punto en el espacio gráfico en el que se debe mostrar una imagen, es solo un punto 3D y un puntero de memoria al primer píxel de la imagen a utilizar. TAL VEZ también incluye las dimensiones de la porción del archivo de imagen de sprite que se utilizará, ya sea en términos gráficos o de memoria. Esta información es muy económica de cambiar, por ejemplo, para cambiar la imagen o la ubicación del sprite, y se puede hacer sin cargar una nueva imagen cada vez, lo que aumenta drásticamente el rendimiento del programa subyacente para manipular y mostrar las partes adecuadas de la imagen. imágenes adecuadas para representar una "escena" de IU completa.

KeithS
fuente
3

Las Characterinstancias de rango ASCII en Smalltalk son pesos mosca.

Cuando evalúas algo como Character space, Character class >> #value:ejecuta:

value: anInteger 
    "Answer the Character whose value is anInteger."

    anInteger > 255 ifTrue: [^self basicNew setValue: anInteger].
    ^ CharacterTable at: anInteger + 1.

La variable de clase CharacterTablese inicializa así:

initialize
    "Create the table of unique Characters, and DigitsValues."
    "Character initializeClassificationTable"

    CharacterTable ifNil: [
        "Initialize only once to ensure that byte characters are unique"
        CharacterTable := Array new: 256.
        1 to: 256 do: [:i | CharacterTable
            at: i
            put: (self basicNew setValue: i - 1)]].
    self initializeDigitValues

Entonces, cuando crea una Cadena, el rango ASCII Charactervendrá en CharacterTablelugar de ser creado nuevamente cada vez.

Frank Shearar
fuente
3

El propósito de usar el patrón de peso mosca es evitar la inicialización innecesaria de objetos y así ahorrar espacio. Según lo definido por GOF , un objeto puede tener dos estados, el estado intrínseco y el extrínseco:

  • Estado intrínseco: se almacena en el peso mosca; consiste en información que es independiente del contexto de los pesos mosca, por lo que es compartible.
  • Estado extrínseco: depende y varía según el contexto del peso mosca y, por lo tanto, no se puede compartir. Los objetos del cliente son responsables de pasar el estado extrínseco al peso mosca cuando lo necesita.

Suponiendo que queremos desarrollar una aplicación de editor de texto simple donde cada columna contiene todas las filas del texto y la fila puede contener caracteres.

El dilema aquí es cómo diseñar la clase de personaje. El char cdentro de la clase de caracteres debería ser la principal (estado intrínseca) objeto. Sin embargo, un carácter puede tener una fuente y un tamaño (estado extrínseco); por lo tanto, necesitamos almacenar su estado extrínseco en la fila (cliente) y acceder a él cuando sea necesario. Para este propósito, se crean dos listas que almacenan las fuentes y los tamaños.

Siguiendo el patrón Flyweight, el personaje ahora es reutilizable y los objetos se referencian desde una lista específica de objetos (el grupo de peso mosca) que contiene todos los símbolos ( Characterobjetos) ASCII .

Esto es lo que describí visualmente:

ingrese la descripción de la imagen aquí

Para imprimir 'hola', solo Characterse necesitan 4 objetos, en lugar de 5. Una vez que se cambia la fuente, no se requieren objetos nuevos; tenga en cuenta que esto no sería posible si hubiéramos almacenado el estado extrínseco en la clase Carácter, por ejemplo,

class Character
{
    char c;
    int Size;
    Font font;

    ....
}

La aplicación de este patrón en grandes conjuntos de datos conduciría a optimizaciones significativas en la complejidad de la memoria de la aplicación y la reutilización de objetos.

Menelaos Kotsollaris
fuente