stringtranslate.com

Patrón de peso mosca

Una captura de pantalla del paquete Writer de LibreOffice.
Los editores de texto, como LibreOffice Writer , a menudo utilizan el patrón flyweight.

En programación informática , el patrón de diseño de software flyweight se refiere a un objeto que minimiza el uso de memoria al compartir algunos de sus datos con otros objetos similares. El patrón flyweight es uno de los veintitrés patrones de diseño GoF conocidos . [1] Estos patrones promueven el diseño de software orientado a objetos flexible, que es más fácil de implementar, cambiar, probar y reutilizar.

En otros contextos, la idea de compartir estructuras de datos se denomina " hash consing" .

El término fue acuñado por primera vez, y la idea fue explorada ampliamente, por Paul Calder y Mark Linton en 1990 [2] para manejar eficientemente la información de glifos en un editor de documentos WYSIWYG . [3] Sin embargo, ya se utilizaban técnicas similares en otros sistemas, ya en 1988. [4]

Descripción general

El patrón flyweight es útil cuando se trabaja con una gran cantidad de objetos que comparten elementos simples repetidos que utilizarían una gran cantidad de memoria si se los incorporara individualmente. Es común mantener datos compartidos en estructuras de datos externas y pasarlos a los objetos temporalmente cuando se los utiliza.

Un ejemplo clásico son las estructuras de datos que se utilizan para representar caracteres en un procesador de textos . De manera sencilla, cada carácter de un documento podría tener un objeto glifo que contenga el contorno de la fuente, las métricas de la fuente y otros datos de formato. Sin embargo, esto utilizaría cientos o miles de bytes de memoria para cada carácter. En cambio, cada carácter puede tener una referencia a un objeto glifo compartido por cada instancia del mismo carácter en el documento. De esta manera, solo es necesario almacenar internamente la posición de cada carácter.

Como resultado, los objetos de peso mosca pueden: [5]

Los clientes pueden reutilizar Flyweightobjetos y pasarlos al estado extrínseco según sea necesario, reduciendo la cantidad de objetos creados físicamente.

Estructura

Un ejemplo de diagrama de clase y secuencia UML para el patrón de diseño Flyweight. [6]

El diagrama de clases UML anterior muestra:

El diagrama de secuencia muestra las siguientes interacciones en tiempo de ejecución :

  1. El Clientobjeto llama getFlyweight(key)a FlyweightFactory, que devuelve un Flyweight1objeto.
  2. Después de llamar operation(extrinsicState)al Flyweight1objeto devuelto, Clientvuelve a llamar getFlyweight(key)a FlyweightFactory.
  3. Devuelve FlyweightFactoryel objeto ya existente Flyweight1.

Detalles de implementación

Existen múltiples formas de implementar el patrón flyweight. Un ejemplo es la mutabilidad: si los objetos que almacenan el estado flyweight extrínseco pueden cambiar.

Los objetos inmutables se comparten fácilmente, pero requieren la creación de nuevos objetos extrínsecos cada vez que se produce un cambio de estado. Por el contrario, los objetos mutables pueden compartir el estado. La mutabilidad permite una mejor reutilización de los objetos mediante el almacenamiento en caché y la reinicialización de objetos antiguos que no se utilizan. El uso compartido suele ser inviable cuando el estado es muy variable.

Otras preocupaciones principales incluyen la recuperación (cómo el cliente final accede al flyweight), el almacenamiento en caché y la simultaneidad .

Recuperación

La interfaz de fábrica para crear o reutilizar objetos flyweight suele ser una fachada para un sistema subyacente complejo. Por ejemplo, la interfaz de fábrica se implementa comúnmente como un singleton para proporcionar acceso global para crear objetos flyweight.

En términos generales, el algoritmo de recuperación comienza con una solicitud de un nuevo objeto a través de la interfaz de fábrica.

La solicitud se envía normalmente a una memoria caché adecuada en función del tipo de objeto que sea. Si la solicitud se cumple con un objeto en la memoria caché, es posible que se reinicie y se devuelva. De lo contrario, se crea una instancia de un nuevo objeto. Si el objeto se divide en varios subcomponentes extrínsecos, se unirán antes de que se devuelva el objeto.

Almacenamiento en caché

Hay dos formas de almacenar en caché objetos flyweight: cachés mantenidos y no mantenidos.

Los objetos con un estado muy variable se pueden almacenar en caché con una estructura FIFO . Esta estructura mantiene los objetos no utilizados en la caché, sin necesidad de realizar búsquedas en ella.

Por el contrario, las cachés sin mantenimiento tienen menos sobrecarga inicial: los objetos para las cachés se inicializan en masa en el momento de la compilación o el inicio. Una vez que los objetos llenan la caché, el algoritmo de recuperación de objetos puede tener más sobrecarga asociada que las operaciones de inserción/extracción de una caché con mantenimiento.

Al recuperar objetos extrínsecos con estado inmutable, simplemente se debe buscar en la memoria caché un objeto con el estado deseado. Si no se encuentra dicho objeto, se debe inicializar uno con ese estado. Al recuperar objetos extrínsecos con estado mutable, se debe buscar en la memoria caché un objeto no utilizado para reinicializarlo si no se encuentra ningún objeto utilizado. Si no hay ningún objeto no utilizado disponible, se debe crear una instancia de un nuevo objeto y agregarlo a la memoria caché.

Se pueden utilizar cachés independientes para cada subclase única de objeto extrínseco. Se pueden optimizar varios cachés por separado, asociando un algoritmo de búsqueda único con cada caché. Este sistema de almacenamiento en caché de objetos se puede encapsular con el patrón de cadena de responsabilidad , que promueve un acoplamiento flexible entre componentes.

Concurrencia

Se debe tener en cuenta una consideración especial cuando se crean objetos flyweight en varios subprocesos. Si la lista de valores es finita y se conoce de antemano, los flyweights se pueden instanciar con anticipación y recuperar de un contenedor en varios subprocesos sin contención. Si los flyweights se instancian en varios subprocesos, hay dos opciones:

  1. Haga que la instanciación de flyweight sea de un solo subproceso, introduciendo así contención y garantizando una instancia por valor.
  2. Permitir que subprocesos simultáneos creen múltiples instancias flyweight, eliminando así la contención y permitiendo múltiples instancias por valor.

Para permitir el intercambio seguro entre clientes y subprocesos, los objetos flyweight se pueden convertir en objetos de valor inmutables , donde dos instancias se consideran iguales si sus valores son iguales.

Este ejemplo de C# 9 utiliza registros [7] para crear un objeto de valor que representa sabores de café:

registro público CoffeeFlavours ( sabor de cadena );   

Ejemplo en C#

En este ejemplo, cada instancia de la MyObjectclase utiliza una Pointerclase para proporcionar datos.

// Define un objeto Flyweight que se repite. public class Flyweight { public string Name { get ; set ; } public string Location { get ; set ; } public string Website { get ; set ; } public byte [] Logo { get ; set ; } }                              clase pública estática Pointer { pública estática de solo lectura Flyweight Company = new Flyweight { "Abc" , "XYZ" , "www.example.com" }; }              clase pública MyObject { cadena pública Nombre { obtener ; establecer ; } cadena pública Empresa = > Puntero.Empresa.Nombre ; }              

Ejemplo en C++

La biblioteca de plantillas estándar de C++ ofrece varios contenedores que permiten asignar objetos únicos a una clave. El uso de contenedores ayuda a reducir aún más el uso de memoria al eliminar la necesidad de crear objetos temporales.

#include <iostream> #include <mapa> #include <cadena>   // Las instancias de Tenant serán la clase Flyweights Tenant { public : Tenant ( const std :: string & name = "" ) : m_name ( name ) {}           std :: string nombre () const { return m_nombre ; } privado : std :: string m_nombre ; };        // El registro actúa como una fábrica y un caché para los objetos flyweight de Tenant class Registry { public : Registry () : inquilinos () {}       Inquilino & findByName ( const std :: string & nombre ) { if ( ! inquilinos.contiene ( nombre )) { inquilinos [ nombre ] = Inquilino { nombre } ; } return inquilinos [ nombre ]; } private : std :: map < std :: string , Inquilino > inquilinos ; } ;                 // El apartamento asigna un inquilino único a su número de habitación. class Apartment { public : Apartment () : m_occupants (), m_registry () {}        void addOccupant ( const std :: string & nombre , int habitación ) { m_occupants [ habitación ] = & m_registry.findByName ( nombre ) ; }           void inquilinos () { for ( const auto & i : m_ocupantes ) { const int & habitación = i . primero ; const auto & inquilino = i . segundo ; std :: cout << inquilino -> nombre () << " ocupa habitación " << habitación << std :: endl ; } } privado : std :: map < int , Inquilino *> m_ocupantes ; Registro m_registry ; };                                   int main () { Apartamento apartamento ; departamento . addOccupant ( "David" , 1 ); departamento . addOccupant ( "Sara" , 3 ); departamento . agregarOcupante ( "George" , 2 ); departamento . agregarOcupante ( "Lisa" , 12 ); departamento . agregarOcupante ( "Michael" , 10 ); departamento . inquilinos ();                devuelve 0 ; } 

Ejemplo en PHP

<?phpclase  SaborCafé  {  matriz estática  privada $CACHE = [];     función  privada __construct ( cadena privada  $nombre ) {}    función pública estática  interna ( cadena $nombre ) : self { self :: $CACHE [ $nombre ] ??= new self ( $nombre ); return self :: $CACHE [ $nombre ]; }             función estática  pública flavorsInCache () : int { return count ( self :: $CACHE ); }        función  pública __toString () :  cadena  {  return  $this -> nombre ;  }}clase  Orden  {  función  privada __construct (  private  CoffeeFlavour  $sabor ,  private  int  $tableNumber  )  {}  función estática  pública create ( string $nombreSabor , int $numeroTabla ) : self { $sabor = SaborCafé :: intern ( $nombreSabor ); return new self ( $sabor , $numeroTabla ); }                función  pública __toString () :  cadena  {  devolver  "Sirviendo { $this -> flavor } a la mesa { $this -> tableNumber } " ;  } }clase  Cafetería  {  matriz  privada $orders  =  [];  función  pública tomarOrden ( string  $sabor ,  int  $numeroDeTabla )  {  $this -> pedidos []  =  Pedido :: crear ( $sabor ,  $numeroDeTabla );  }  función  pública servicio ()  {  imprimir ( implosionar ( PHP_EOL ,  $ this -> pedidos ) .PHP_EOL ); } } $tienda  =  new  CoffeeShop (); $tienda -> takeOrder ( "Capuchino" ,  2 ); $tienda -> takeOrder ( "Frappe" ,  1 ); $tienda -> takeOrder ( "Espresso" ,  1 ); $tienda -> takeOrder ( "Frappe" ,  897 ); $tienda -> takeOrder ( "Capuchino" ,  97 ); $ tienda - > takeOrder ( "Frappe" ,  3 ); $tienda -> takeOrder ( "Espresso" ,  3 ); $tienda -> takeOrder ( "Capuchino" ,  3 ); $tienda -> takeOrder ( "Espresso" ,  96 ); $tienda -> takeOrder ( "Frappe" ,  552 ); $tienda -> takeOrder ( "Capuchino" ,  121 ); $tienda -> takeOrder ( "Espresso" ,  121 ); $tienda -> servicio (); print ( "Objetos CoffeeFlavor en caché: " . CoffeeFlavour :: saboresInCache () . PHP_EOL );

Véase también

Referencias

  1. ^ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Patrones de diseño: elementos de software reutilizable orientado a objetos. Addison Wesley. pp. 195ff. ISBN 978-0-201-63361-0.{{cite book}}: CS1 maint: multiple names: authors list (link)
  2. ^ Gamma, Erich ; Richard Helm ; Ralph Johnson ; John Vlissides (1995). Patrones de diseño: elementos de software orientado a objetos reutilizable . Addison-Wesley . págs. 205–206. ISBN 978-0-201-63361-0.
  3. ^ Calder, Paul R.; Linton, Mark A. (octubre de 1990). "Glifos: objetos de peso ligero para interfaces de usuario". Actas del tercer simposio anual ACM SIGGRAPH sobre software y tecnología de interfaz de usuario - UIST '90 . Tercer simposio anual ACM SIGGRAPH sobre software y tecnología de interfaz de usuario. Snowbird, Utah, Estados Unidos. págs. 92–101. doi :10.1145/97924.97935. ISBN 0-89791-410-4.
  4. ^ Weinand, Andre; Gamma, Erich; Marty, Rudolf (1988). ET++—un marco de trabajo de aplicación orientado a objetos en C++ . OOPSLA (Object-Oriented Programming Systems, Languages ​​and Applications). San Diego, California, Estados Unidos. pp. 46–57. CiteSeerX 10.1.1.471.8796 . doi :10.1145/62083.62089. ISBN  0-89791-284-5.
  5. ^ "Implementación de patrones Flyweight en Java". Developer.com . 28 de enero de 2019 . Consultado el 12 de junio de 2021 .
  6. ^ "El patrón de diseño Flyweight: estructura y colaboración". w3sDesign.com . Consultado el 12 de agosto de 2017 .
  7. ^ BillWagner. "Registros - Referencia de C#". docs.microsoft.com . Consultado el 12 de junio de 2021 .