Acabando con el patrón singleton

Anuncio
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
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 ([email protected])
55/55
Descargar