Sigleton (Patrones de diseño I)

C++ es mi lenguaje favorito. Por eso ademas de escribir en el quiero escribir sobre el, una forma de hacerlo es escribir sobre Patrones de diseño, que como seguramente ya sabes son formas de escribir código para resolver algún problema que se presenta con regularidad. Estos problemas son de desarrollo de software y no de algo aplicable directamente.

Es decir a veces el hecho de programar es por si mismo un problema, aqui es donde tienen utilidad los patrones de diseño. Veamos el primer ejemplo:

Seguro te haz topado con el siguiente problema, imagina que debes crear una clase que guarde en un archivo o en una base de datos lo que el sistema va haciendo, una especie de log, hay que tener cuidado de no instanciar mas de una vez la clase porque no queremos a dos objetos accesando a la vez al mismo archivo o BD, digamos que se ve mas o menos asi:

class log {
	public:
	log()
	{
		// Aqui abrimos el archivo o abrimos la conexion a la DB
	}
 
	virtual ~log()
	{
	}
 
	void Add(string Linea)
	{
		// Aqui almacenamos el texto en el archivo o la DB
	}
};

Tenemos muchas opciones para usarlo, por ejemplo podemos crear una variable global asi:

log MiLog;

en nuestro primer archivo digamos main.cpp y en cada archivo y clase que vallamos creando ponemos un:

extern log MiLog;

lo cual le indica al compilador que no valla a crear una nueva instancia de nuestro objeto MiLog, en lugar de ello use la primera instancia que se creo. Mmmm, funciona pero la variables globales no son muy bonitas, y son difíciles de mantener.

Tambien podríamos arrastrar la referencia a la variable por todos lados, pasándola como parámetro en cada función que vallamos creando, o como una propiedad de todas las clases que vallamos creando. Algo asi como:

void main (void) {
	log * MiLog = new log();
 
	MiFucion(5, "hola", MiLog);
 
	miclase MiClase(3.14, MiLog);
	MiClase.miMetodo(3);
 
}
 
int MiFucion (int Parametro1, string Parametro2, log * Milog) {
	MiLog->Add("Guarda este texto");
}
 
class miClase {
private:
	float Propiedad1;
	log * MiLog;
public:
	miClase(float Propiedad1, log * MiLog) {
		this->Propiedad1 = Propiedad1;
		this->MiLog = MiLog;
	}
 
	void miMetodo(int Parametro1) {
		MiLog->Add("Guarda este texto");
	}
}

Funciona, pero como ves hay que poner la referencia en todos lados, en cada clase y funcion en donde queramos ocupar nuestro log.

Otra forma de hacerlo es creando un Singleton. Un Singleton es una clase que:

  • Solo puede instanciarse una vez.
  • Solo tiene un punto de acceso a ella.

Es decir si nuestra clase log fuera un Singleton, solo se intanciaria una vez, con lo que  una y solo una instancia del objeto podría acceder a nuestro archivo o DB y ademas tendriamos un solo punto de acceso a ella, como si fuera una variable global, la cual no tenemos que arrastrar o pasar como parámetro cada vez.

Hay muchas formas de hacer un Singleton, sobre todo en C++.

La que mas me gusta es la siguiente, veamos el código:

class log {
public:
	static log * instance()
	{
		static log * Yo = NULL;
		if (Yo == NULL) {
			Yo = new log();
		}
		return Yo;
	}
 
	virtual ~log()
	{
 
	}
private:
	log()
	{
		// Aqui abrimos el archivo o abrimos la conexion a la DB
	}
};

Lo primero que hay que notar es como el constructor es privado, por lo que no podemos crear una instancia del objeto. Solo el mismo puede instanciarse :). Luego hay que ver que hemos creado el metodo estatico intance() que retorna un apuntador de tipo log. Dentro hay una variable estática llamada Yo que, si recordamos como funcionan las variables estáticas, nos damos cuanta de que solo se crea una vez y el valor no se pierde. La primera vez Yo vale NULL y la instanciamos por primera vez, pero el resto de las veces Yo ya esta instanciada.

Nuestra clase Singleton se usa asi:

log::instance()->Add("Guarda este texto");

Tomando el ejemplo anterior se veria asi:

void main (void) {
 
	MiFucion(5, "hola");
 
	miclase MiClase(3.14);
	MiClase.miMetodo(3);
 
}
 
int MiFucion (int Parametro1, string Parametro2, log * Milog) {
	log::instance()->Add("Guarda este texto");
}
 
class miClase {
	private:
	float Propiedad1;
public:
	miClase(float Propiedad1, log * MiLog) {
	this->Propiedad1 = Propiedad1;
}
 
	void miMetodo(int Parametro1) {
		log::instance()->Add("Guarda este texto");
	}
}

¿Apoco no es mas padre que una variable global? O que andar llevando la referencia por todo el código. El Singleton en uno de los patrones mas utilizados y como comentaba hay muchas formas de hacerlos. No solo en C++ sino tambien en otros lenguajes. Dale un vistazo a la Wiki.

 

Eric Ruiz Osornio (Zeru). Instituto Politécnico Nacional, México. Gamer de StarCraft2, coder y administrador de proyectos.

Facebook Twitter 

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *