Acabando con el patrón singleton Acabando con el patrón singleton using std::cpp 2014 J. Daniel Garcia Grupo ARCOS Universidad Carlos III de Madrid 28 de Octubre de 2014 cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 1/55 Acabando con el patrón singleton Aviso c Esta obra está bajo una Licencia Creative Commons b e d cb e d – Atribución-NoComercial-SinDerivar 4.0 Internacional. Debes dar crédito en la obra en la forma especificada por el autor o licenciante. El licenciante permite copiar, distribuir y comunicar públicamente la obra. A cambio, esta obra no puede ser utilizada con fines comerciales — a menos que se obtenga el permiso expreso del licenciante. El licenciante permite copiar, distribuir, transmitir y comunicar públicamente solamente copias inalteradas de la obra – no obras derivadas basadas en ella. J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 2/55 Acabando con el patrón singleton Introducción 1 Introducción 2 El patrón Singleton 3 Ejemplos en la biblioteca estándar 4 La sencillez de los singleton 5 Revisitando el patrón 6 Detalles 7 Conclusiones cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 3/55 Acabando con el patrón singleton Introducción Diseño de software Existen dos formas de construir un diseño de software: simplificándolo hasta el punto que resulte obvio que no hay en el errores o complicándolo de tal forma que los errores que haya en el no sean obvios. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 4/55 Acabando con el patrón singleton Introducción Diseño de software Existen dos formas de construir un diseño de software: simplificándolo hasta el punto que resulte obvio que no hay en el errores o complicándolo de tal forma que los errores que haya en el no sean obvios. El primer método es mucho mas difícil. Sir Tony Hoare, Premio Turing 1980. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 4/55 Acabando con el patrón singleton Introducción ¿Importa el diseño? ¿Dónde te gustaría vivir? cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 5/55 Acabando con el patrón singleton Introducción Lenguajes de Patrones 1977: A Pattern Language: Towns, Buildings, Construction. 1979: The timeless Way of buildings. Christopher Alexander. Introduce las nociones de patrón y lenguaje de patrones. Es un libro de arquitectura y edificación. Buscaba definir reglas paso-a-paso para resolver problemas comunes de ingeniería en la creación de edificios y ciudades. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 6/55 Acabando con el patrón singleton Introducción GoF: The Gang of Four 1991: Gamma → Idea de patrones software. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 7/55 Acabando con el patrón singleton Introducción GoF: The Gang of Four 1991: Gamma → Idea de patrones software. 1993: GoF envían un catálogo de patrones al ECOOP. Design Patterns: Abstraction and Reuse of Object-Oriented Design. E. Gamma, R. Helm, R. Johnson, J. Vlissides. ECOOP, LNCS 707, pp. 406–431. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 7/55 Acabando con el patrón singleton Introducción GoF: The Gang of Four 1991: Gamma → Idea de patrones software. 1993: GoF envían un catálogo de patrones al ECOOP. Design Patterns: Abstraction and Reuse of Object-Oriented Design. E. Gamma, R. Helm, R. Johnson, J. Vlissides. ECOOP, LNCS 707, pp. 406–431. 1995: Desing Patterns elements of Reusable Object-Oriented Software. Más de un millón de copias vendidas. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 7/55 Acabando con el patrón singleton Introducción GoF: Catálogo Creación Abstract Factory. Builder. Factory Method. Prototype. Singleton. Proxy. Comportamiento Estructural Adapter. Bridge. Composite. Decorator. Facade. Flyweight. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) Chain of Responsibility. Command. Interpreter. Iterator. Mediator. Memento. Observer. State. Strategy. Template Method. Visitor. 8/55 Acabando con el patrón singleton El patrón Singleton 1 Introducción 2 El patrón Singleton 3 Ejemplos en la biblioteca estándar 4 La sencillez de los singleton 5 Revisitando el patrón 6 Detalles 7 Conclusiones cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 9/55 Acabando con el patrón singleton El patrón Singleton Patrones de creación Un patrón de creación ofrece flexibilidad sobre: ¿Qué debe crearse? ¿Quién lo crea? ¿Cómo se crea? ¿Cuándo se crea? ¿Quién lo destruye? ¿Cómo se destruye? ¿Cuándo se destruye? cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 10/55 Acabando con el patrón singleton El patrón Singleton Singleton Intención: Asegurar que una clase tiene una única instancia y ofrecer un punto de acceso global a la misma. Aplicabilidad: Debe haber exactamente una instancia de la clase, y debe ser accesible a los clientes desde un punto de acceso bien conocido. Cuando una única instancia debe ser extensible (mediante subclases) y los clientes deberían poder extender la instancia sin modificar su código. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 11/55 Acabando con el patrón singleton El patrón Singleton Consecuencias Acceso controlado a la única instancia Encapsula la instancia y controla el acceso de los clientes. Espacio de nombres reducido Evita la polución del espacio de nombres con variables globales. Permite refinar operaciones y representación Se puede subclasificar la clase singleton. Permite un número variable de instancias Se puede configurar un número distinto de 1 con cambios mínimos. Más flexible que operaciones de clase (estáticas) Difícil de cambiar el diseño (poca flexibilidad). cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 12/55 Acabando con el patrón singleton El patrón Singleton Ejemplo canónico single.h single.cpp #ifndef SINGLE_H #define SINGLE_H #include "single.h" #include <iostream> singleton ∗ singleton :: psing = nullptr ; class singleton { public: static singleton ∗ instance() ; void op1(); protected: singleton () ; private: static singleton ∗ psing; }; #endif cb e d – singleton ∗ singleton :: instance() { if (nullptr == psing) { psing = new singleton; } return psing; } singleton :: singleton () { std :: cout << "singleton () " << std :: endl; } void singleton :: op1() { std :: cout << "op1()" << std :: endl; } J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 13/55 Acabando con el patrón singleton El patrón Singleton Preguntas ¿Qué debe crearse? Una única instancia del singleton. ¿Quién lo crea? El cliente que necesita usar el singleton. ¿Cómo se crea? Obteniendo acceso mediante instance. ¿Cuándo se crea? En la primera invoacación a instance. ¿Quién lo destruye? No se destruye. ¿Cómo se destruye? No se destruye. ¿Cuándo se destruye? No se destruye. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 14/55 Acabando con el patrón singleton Ejemplos en la biblioteca estándar 1 Introducción 2 El patrón Singleton 3 Ejemplos en la biblioteca estándar 4 La sencillez de los singleton 5 Revisitando el patrón 6 Detalles 7 Conclusiones cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 15/55 Acabando con el patrón singleton Ejemplos en la biblioteca estándar errno errno.h extern int errno; cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 16/55 Acabando con el patrón singleton Ejemplos en la biblioteca estándar errno errno.h extern int errno; errno.cc int errno; cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 16/55 Acabando con el patrón singleton Ejemplos en la biblioteca estándar errno errno.h extern int errno; errno.cc int errno; Código de usuario void g() { // ... if (read(fd0, buffer , n) < 0) { switch (errno) { // ... } cb}e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 16/55 Acabando con el patrón singleton Ejemplos en la biblioteca estándar errno errno.h extern int errno; errno.cc int errno; Código de usuario read.cc (simplificado) ssize_t read(int fd , void ∗ buf, size_t nb) { if (nbytes == 0) return 0; if (fd < 0) { errno = EBADF; return −1; } if (buf == NULL) { errno = EINVAL; return −1; } void g() { // ... if (read(fd0, buffer , n) < 0) { errno = ENOSYS; switch (errno) { return −1; // ... } } cb}e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 16/55 Acabando con el patrón singleton Ejemplos en la biblioteca estándar Preguntas sobre errno ¿Qué debe crearse? Una única instancia de errno. ¿Quién lo crea? El enlazador. ¿Cómo se crea? Accediendo a la variable de programa. ¿Cuándo se crea? En arranque de programa. ¿Quién lo destruye? No se destruye. ¿Cómo se destruye? No se destruye. ¿Cuándo se destruye? No se destruye. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 17/55 Acabando con el patrón singleton Ejemplos en la biblioteca estándar Los singletons de la biblioteca estándar cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 18/55 Acabando con el patrón singleton Ejemplos en la biblioteca estándar Los singletons de la biblioteca estándar iostream namespace std { extern istream cin; extern ostream cout; extern ostream cerr; extern ostream clog; static ios_base:: Init __ioinit ; } cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 18/55 Acabando con el patrón singleton Ejemplos en la biblioteca estándar Los singletons de la biblioteca estándar globals_io.cc iostream namespace std { extern istream cin; extern ostream cout; extern ostream cerr; extern ostream clog; static ios_base:: Init __ioinit ; } cb e d – namespace std { using istream_buffer = char alignas(istream) [sizeof(istream) ]; using ostream_buffer = char alignas(istream) [sizeof(ostream)]; istream_buffer_t cin ; ostream_buffer_t cout; ostream_buffer_t cerr; ostream_buffer_t clog; // ... } J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 18/55 Acabando con el patrón singleton Ejemplos en la biblioteca estándar Iniciando los singletons ios_base.h namespace std { class ios_base { // ... class Init { public: Init () ; ~ Init () ; private: static atomic<int> refcount; }; // ... }; } cb e d – ios_init.cc namespace std { ios_base:: Init :: Init () { if (refcount.fetch_add(1) == 0) { new (&cout) ostream(&buf_cout_sync); new (&cin) ostream(&buf_cin_sync); new (&cerr) ostream(&buf_cerr_sync); new (&clog) ostream(&buf_cerr_sync); refcount.fetch_add(1); } } J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 19/55 Acabando con el patrón singleton Ejemplos en la biblioteca estándar Preguntas sobre cin, ... ¿Qué debe crearse? Una única instancia de cin, cout, ... ¿Quién lo crea? El cliente si usa el objeto. ¿Cómo se crea? Creando automáticamente el objeto estántico __ioinit. ¿Cuándo se crea? Al ejecutar el constructor de Init por primera vez. ¿Quién lo destruye? El destructor de Init. ¿Cómo se destruye? Al destruir el último objeto __ioinit. ¿Cuándo se destruye? A la finalización del programa. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 20/55 Acabando con el patrón singleton La sencillez de los singleton 1 Introducción 2 El patrón Singleton 3 Ejemplos en la biblioteca estándar 4 La sencillez de los singleton 5 Revisitando el patrón 6 Detalles 7 Conclusiones cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 21/55 Acabando con el patrón singleton La sencillez de los singleton La interfaz de usuario Usando un singleton para errno void f () { // ... if (read(fd0, buffer , n) < 0) { switch(errno_holder::instance()−>value()) { // ... } } } cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 22/55 Acabando con el patrón singleton La sencillez de los singleton Y si tuvieses que escribir esto... Usando un singleton para errno void f () { // ... (∗std :: cout−>instance()) << "Hola" << std::endl; // std :: cout << "Hola" << std :: endl; } } cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 23/55 Acabando con el patrón singleton La sencillez de los singleton Otros problemas La implementación no es tan sencilla como parece: Garantía de destrucción. Problemas de referencias muertas. Orden de destrucción de singletons. Problemas con múltiples hilos. Discutidos en detalle en Modern C++ Design (Alexandrescu). ... que ya empieza a no ser tan moderno. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 24/55 Acabando con el patrón singleton La sencillez de los singleton ¿Y Erich que opina de todo esto? Question: How would you refactor Design Patterns? Answer: ... We have found that the object-oriented design principles and most of the patterns haven’t changed since then. ... When discussing which patterns to drop, we found that we still love them all. (Not really – I’m in favor of dropping Singleton. Its use is almost always a design smell.) Design Patterns 15 Years Later: An Interview with Erich Gamma, Richard Helm, and Ralph Johnson. October, 2009. http://www.informit.com/articles/article.aspx?p=1404056 cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 25/55 Acabando con el patrón singleton Revisitando el patrón 1 Introducción 2 El patrón Singleton 3 Ejemplos en la biblioteca estándar 4 La sencillez de los singleton 5 Revisitando el patrón 6 Detalles 7 Conclusiones cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 26/55 Acabando con el patrón singleton Revisitando el patrón ¿Estás seguro? Tener una única instancia global es una singularidad. ¿Es realmente necesario? Impide copias del objeto. Impide paso y retorno por valor. Pocas veces la restricción tiene sentdio. ¿Una única impresora? ¿Un único log para la aplicación? Más problemático en un mundo concurrente. En el mejor de los casos es una fuente de contención. En el peor de los casos es una fuente de carreras de datos. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 27/55 Acabando con el patrón singleton Revisitando el patrón Un log de eventos evlog.h #ifndef EVLOG_H #define EVLOG_H #include <string> #include <vector> class event_logger { public: event_logger(); ~event_logger(); void log(const std:: string & m); void set_file_name(const std::string & m) { file_name = m; } void set_size(unsigned n) { size = n; } private: void dump(); private: unsigned size = 4; std :: vector<std :: string > buffer ; std :: string file_name {"log. txt " }; }; #endif cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 28/55 Acabando con el patrón singleton Revisitando el patrón Implementando un log de eventos evlog.cpp #include "evlog.h" #include <fstream> using namespace std; event_logger::event_logger() { ofstream file {file_name, ios :: out | ios :: trunc }; file << "Log started" << endl; } event_logger::~event_logger() { if ( buffer . size () > 0) { dump(); } ofstream file {file_name, ios :: app | ios :: out }; file << "Log ended" << endl; } void event_logger::log(const std:: string & m) { if ( buffer . size () >= size) { dump(); } buffer .push_back(m); } cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 29/55 Acabando con el patrón singleton Revisitando el patrón Implementando un log de eventos evlog.cpp void event_logger::dump() { ofstream file {file_name, ios :: app | ios :: out }; for (auto && m : buffer) { file << m << endl; } buffer . clear () ; } cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 30/55 Acabando con el patrón singleton Revisitando el patrón En ocasiones veo singletons ... incluso donde no los hay! Nada debería impedir el uso de múltiples logs de eventos. Aún así tomemos el caso como ejemplo. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 31/55 Acabando con el patrón singleton Revisitando el patrón Un log de eventos único evlog.h #ifndef EVLOG_H #define EVLOG_H #include <string> #include <vector> class event_logger { public: static event_logger & instance(); ~event_logger(); event_logger(const event_logger &) = delete; event_logger(event_logger &&) = delete; event_logger & operator=(const event_logger &) = delete; event_logger & operator=(event_logger &&) = delete; private: event_logger(); public: void log(const std:: string & m); void set_file_name(const std::string & m) { file_name = m; } void set_size(unsigned n) { size = n; } cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 32/55 Acabando con el patrón singleton Revisitando el patrón Un log de eventos único evlog.h private: void dump(); private: unsigned size = 4; std :: vector<std :: string > buffer ; std :: string file_name {"log. txt " }; }; #endif cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 33/55 Acabando con el patrón singleton Revisitando el patrón Implementando un log de eventos único evlog.cpp #include "evlog.h" #include <fstream> using namespace std; event_logger & event_logger::instance() { static event_logger ev; return ev; } event_logger::event_logger() { ofstream file {file_name, ios :: out | ios :: trunc }; file << "Log started" << endl; } event_logger::~event_logger() { if ( buffer . size () > 0) { dump(); } ofstream file {file_name, ios :: app | ios :: out }; file << "Log ended" << endl; } cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 34/55 Acabando con el patrón singleton Revisitando el patrón Implementando un log de eventos único evlog.cpp void event_logger::log(const std:: string & m) { if ( buffer . size () >= size) { dump(); } buffer .push_back(m); } void event_logger::dump() { ofstream file {file_name, ios :: app | ios :: out }; for (auto && m : buffer) { file << m << endl; } buffer . clear () ; } cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 35/55 Acabando con el patrón singleton Revisitando el patrón Acceso a la instancia Acceso controlado a la única instancia. El singleton ofrece un mecanismo de acceso global. Una variable global también ofrece un mecanismo de acceso global. El singleton permite controlar cada acceso al único objeto global. Ejemplo: Registrar cada evento de uso. Pero nada impide que el cliente almacene una referencia al objeto global. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 36/55 Acabando con el patrón singleton Revisitando el patrón Un log de eventos global evlog.h #ifndef EVLOG_H #define EVLOG_H #include <string> namespace logging { void log(const std:: string & name); void set_file_name(const std::string & m); void set_size(unsigned n); } #endif cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 37/55 Acabando con el patrón singleton Revisitando el patrón Implementando un log de eventos global evlog.cpp #include "evlog.h" #include <fstream> #include <vector> namespace logging { using namespace std; namespace { unsigned size = 4; std :: vector<std :: string > buffer ; std :: string file_name = "log. txt " ; void dump() { ofstream file {file_name, ios :: app | ios :: out }; for (auto && m : buffer) { file << m << endl; } buffer . clear () ; } } // Anonymous namespace cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 38/55 Acabando con el patrón singleton Revisitando el patrón Implementando un log de eventos global evlog.cpp void log(const std:: string & m) { if ( buffer . size () >= size) { dump(); } buffer .push_back(m); } void set_file_name(const std::string & n) { file_name = n; } void set_size(unsigned n) { size = n; } cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 39/55 Acabando con el patrón singleton Revisitando el patrón Implementando un log de eventos global evlog.cpp namespace { class init { public: init () { ofstream file {file_name, ios :: out | ios :: trunc }; file << "Log started" << endl; } ~ init () { if ( buffer . size () > 0) { dump(); } ofstream file {file_name, ios :: app | ios :: out }; file << "Log ended" << endl; } }; init initializer ; } // Anonymous namespace } // namespace logging cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 40/55 Acabando con el patrón singleton Revisitando el patrón Otras consideraciones Espacio de nombres reducidos: Un uso efectivo de los namespace evita la polución del espacio de nombres global. Refinamiento de operaciones y representación. Se puede ocultar destrás de una interfaz muy simple. Dependencia entre singletons. Se puede gestionar mediante un gestor de inicicación único. Después de todo si hay dependencia entre singletons estos deberían estar acoplados. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 41/55 Acabando con el patrón singleton Detalles 1 Introducción 2 El patrón Singleton 3 Ejemplos en la biblioteca estándar 4 La sencillez de los singleton 5 Revisitando el patrón 6 Detalles 7 Conclusiones cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 42/55 Acabando con el patrón singleton Detalles Iniciación perezosa 6 Detalles Iniciación perezosa ¿Y qué pasa con la concurrencia? cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 43/55 Acabando con el patrón singleton Detalles Iniciación perezosa Problema Se debería poder diferir la iniciación al primer uso. Y evitar la iniciación si no se llega a usar. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 44/55 Acabando con el patrón singleton Detalles Iniciación perezosa Problema Se debería poder diferir la iniciación al primer uso. Y evitar la iniciación si no se llega a usar. La solución: otro nivel adicional de indirección. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 44/55 Acabando con el patrón singleton Detalles Iniciación perezosa Problema Se debería poder diferir la iniciación al primer uso. Y evitar la iniciación si no se llega a usar. La solución: otro nivel adicional de indirección. All problems in computer science can be solved with an additional level of indirection. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 44/55 Acabando con el patrón singleton Detalles Iniciación perezosa Problema Se debería poder diferir la iniciación al primer uso. Y evitar la iniciación si no se llega a usar. La solución: otro nivel adicional de indirección. All problems in computer science can be solved with an additional level of indirection. ... except the problems of too many levels of indirection (David Wheeler). cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 44/55 Acabando con el patrón singleton Detalles Iniciación perezosa Problema Se debería poder diferir la iniciación al primer uso. Y evitar la iniciación si no se llega a usar. La solución: otro nivel adicional de indirección. All problems in computer science can be solved with an additional level of indirection. ... except the problems of too many levels of indirection (David Wheeler). Añadiendo otro nivel de indirección. Solución trivial: memoria dinámica. Pero mejor evitar la memoria dinámica ¿no? cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 44/55 Acabando con el patrón singleton Detalles Iniciación perezosa Una interfaz sencilla evlog.h #ifndef EVLOG_H #define EVLOG_H #include <string> namespace logging { void log(const std:: string & name); void set_file_name(const std::string & m); void set_size(unsigned n); } #endif cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 45/55 Acabando con el patrón singleton Detalles Iniciación perezosa Una clase invisible, pero normal evlog.cpp #include "evlog.h" #include <fstream> #include <vector> namespace logging { using namespace std; namespace { class event_logger { public: event_logger(); ~event_logger(); void log(const string & m); void set_file_name(const string & n) { file_name=n; } void set_size(unsigned s) { size=s; } private: void dump(); private: unsigned size = 4; vector<string> buffer ; string file_name = "log. txt " ; }; cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 46/55 Acabando con el patrón singleton Detalles Iniciación perezosa Una clase invisible, pero normal evlog.cpp event_logger::event_logger() { ofstream file {file_name, ios :: out | ios :: trunc }; file << "Log started" << endl; } event_logger::~event_logger() { if ( buffer . size () > 0) { dump(); } ofstream file {file_name, ios :: app | ios :: out }; file << "Log ended" << endl; } void event_logger::log(const string & m) { if ( buffer . size () >= size) { dump(); } buffer .push_back(m); } cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 47/55 Acabando con el patrón singleton Detalles Iniciación perezosa Una clase invisible, pero normal evlog.cpp void event_logger::dump() { ofstream file {file_name, ios :: app | ios :: out }; for (auto && m : buffer) { file << m << endl; } buffer . clear () ; } cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 48/55 Acabando con el patrón singleton Detalles Iniciación perezosa otro nivel de indirección evlog.cpp char logger alignas(event_logger) [sizeof(event_logger)]; event_logger ∗ plogger = nullptr; struct init { init () { plogger = new (&logger) event_logger{}; } ~ init () { plogger−>~event_logger(); plogger = nullptr ; } }; void do_init () { static init i ; } } // Anonymous namespace void log(const std:: string & m) { do_init () ; cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 49/55 Acabando con el patrón singleton Detalles Iniciación perezosa El resto es simple evlog.cpp } void set_file_name(const std::string & n) { do_init () ; plogger−>set_file_name(n); } void set_size(unsigned n) { do_init () ; plogger−>set_size(n); } } // namespace logging cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 50/55 Acabando con el patrón singleton Detalles ¿Y qué pasa con la concurrencia? 6 Detalles Iniciación perezosa ¿Y qué pasa con la concurrencia? cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 51/55 Acabando con el patrón singleton Detalles ¿Y qué pasa con la concurrencia? Iniciación concurrente de variables estáticas Section 6.7.4: If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. Olvídate del Double Checked Locking Pattern. Todavía tienes que hacer que event_logger sea thread-safe. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 52/55 Acabando con el patrón singleton Conclusiones 1 Introducción 2 El patrón Singleton 3 Ejemplos en la biblioteca estándar 4 La sencillez de los singleton 5 Revisitando el patrón 6 Detalles 7 Conclusiones cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 53/55 Acabando con el patrón singleton Conclusiones Conclusiones Los patrones de diseño son buenas prácticas usadas recurrentemente. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 54/55 Acabando con el patrón singleton Conclusiones Conclusiones Los patrones de diseño son buenas prácticas usadas recurrentemente. ¿Es singleton patrón o anti-patrón? cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 54/55 Acabando con el patrón singleton Conclusiones Conclusiones Los patrones de diseño son buenas prácticas usadas recurrentemente. ¿Es singleton patrón o anti-patrón? La biblioteca estándar no lo usa ni una sola vez. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 54/55 Acabando con el patrón singleton Conclusiones Conclusiones Los patrones de diseño son buenas prácticas usadas recurrentemente. ¿Es singleton patrón o anti-patrón? La biblioteca estándar no lo usa ni una sola vez. Hay soluciones alternativas. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 54/55 Acabando con el patrón singleton Conclusiones Conclusiones Los patrones de diseño son buenas prácticas usadas recurrentemente. ¿Es singleton patrón o anti-patrón? La biblioteca estándar no lo usa ni una sola vez. Hay soluciones alternativas. En el fondo el singleton es una manera enrevesada de simular una variable global. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 54/55 Acabando con el patrón singleton Conclusiones Conclusiones Los patrones de diseño son buenas prácticas usadas recurrentemente. ¿Es singleton patrón o anti-patrón? La biblioteca estándar no lo usa ni una sola vez. Hay soluciones alternativas. En el fondo el singleton es una manera enrevesada de simular una variable global. Pero C++ ya tiene variables globales. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 54/55 Acabando con el patrón singleton Conclusiones Conclusiones Los patrones de diseño son buenas prácticas usadas recurrentemente. ¿Es singleton patrón o anti-patrón? La biblioteca estándar no lo usa ni una sola vez. Hay soluciones alternativas. En el fondo el singleton es una manera enrevesada de simular una variable global. Pero C++ ya tiene variables globales. Piensa en la pesadilla sintáctica sin cout fuese un singleton. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 54/55 Acabando con el patrón singleton Conclusiones Conclusiones Los patrones de diseño son buenas prácticas usadas recurrentemente. ¿Es singleton patrón o anti-patrón? La biblioteca estándar no lo usa ni una sola vez. Hay soluciones alternativas. En el fondo el singleton es una manera enrevesada de simular una variable global. Pero C++ ya tiene variables globales. Piensa en la pesadilla sintáctica sin cout fuese un singleton. Si no te fías de mi, fíate de Erich Gamma. cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 54/55 Acabando con el patrón singleton Conclusiones Acabando con el patrón singleton using std::cpp 2014 J. Daniel Garcia Grupo ARCOS Universidad Carlos III de Madrid 28 de Octubre de 2014 cb e d – J. Daniel Garcia – ARCOS@UC3M (josedaniel.garcia@uc3m.es) 55/55