Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





El Patron Singleton No Sirve Para Nada

Iniciado por tamat, 11 de Julio de 2003, 09:24:36 PM

« anterior - próximo »

tamat

 Resulta que en mi "engine" tengo un manager de texturas que es una clase singleton, tambien tengo una consola rollo quake que obviamente tambien es singleton, para el tema del GUI tengo una widget factory que tambieen es singleton, total, que hoy de camino a la farmacia para comprar aspirinas me ha dado por pensar - ¿y para que tanto singleton?- si total, cada vez que necesito utilizar una de esas funcionalidades necesito encontrar la instancia (o pedirla que para algo es singleton) y luego usarla, ¿no sería más facil usar un namespace y meterlo todo dentro, con variables globales y funciones de C de toda la vida? Si es que me complico la vida solo!  (nooo) .

Total, que he seguido dandole vueltas y ahora veo que TODAS mis clases singleton son inutiles, TODAS mejorarían si las hubiera metido en un namespace y no fueran metodos de una clase, ni que decir de lo que ganaría en tiempo de ejecución y la de errores que me libraría.

En fin, mi pregunta es ¿sirve para algo el patron singleton? ¿existe algun contexto en el que el uso de un patron singleton justifique no usar un namespace con variables globales?

Ahí queda.

(sonoro)
Por un stratos menos tenso

CoLSoN2

 hmm
no me seas cutrón, programa orientado a objetos xDDDDD
un namespace con variables globales y funciones C¿? k mariconada es esa?
los patterns son soluciones a problemas típicos en lo que a diseño orientado a objetos se refiere. Si no programas orientado a objetos ya es otra historia.
Manuel F. Lara
Descargar juegos indie  - blog sobre juegos indie y casual
El Desarrollo Personal.com  - blog sobre productividad, motivación y espíritu emprendedor

tamat

 Pero a ver melon, deja de mitificar tanto el ++, se supone que aqui tratamos de ser practicos, vale que meter todo en su clasecita está muy bien y queda precioso en el visual pero si luego a la hora de usarlo pierdes tiempo buscando la instancia o replicando su dirección por todas partes pues ya me diras que utilidad tienen.

Yo hablo de mi experiencia personal, usaba el patron singleton para aquellas cosas que debian ser unicas y me sorprendia de ver en todas las librerias que me bajaba que habian montones de funciones encapsuladas dentro de un namespace, no de una clase, y ahora me doy cuenta.

Yo solo pregunto de un contexto donde una cosa vaya mejor que la otra, no hablo de lo bonito y precioso del cuadradito que desplega el arbol de una clase sino de si realmente me va a ayudar.

Ahí queda.
Por un stratos menos tenso

CoLSoN2

 no mitifico c++ sino la poo. si quieras programar orientado a objetos usa patterns, si kieres hacer gayadas con namespaces, úsalos XD
pero aver, tanto si tienes un singleton como un namespace con cosas, tienes ke acer el include al fichero, de eso no te libras. y luego con el singleton es simplemente
TextureMgr->method();
si prefieres acer
nspace::method();
pos allá tu, cada uno con su estilo
Manuel F. Lara
Descargar juegos indie  - blog sobre juegos indie y casual
El Desarrollo Personal.com  - blog sobre productividad, motivación y espíritu emprendedor

tamat

 Pero es que para hacer eso que tu dices TextureMgr->method(); necesitas tener una variable TextureMgr con la direccion adecuada, lo que requiere una inicialización previa, y si nos encontramos en un punto del codigo donde no está accesible esa instancia tendremos que apelar a la funcion estatica del patron singleton para sonsacarsela, un proceso que usando un namespace desaparece por completo ya que con tener la libreria incluida basta.
Por un stratos menos tenso

Astat

 Me parece que los singleton son solo utiles de verdad para cuando formas parte de un equipo de programadores grande. Osea, para controlar un poco el tema de los objetos importantes y tal, y que nadie se le vaya la olla creando cosas que no debe crear.

Para el "desarrollador solitario" me parece que como hacerte pajas con un guante de esparto, vale estas haciendo algo que te gusta, pero de una forma dolorosa  :P

Que conste que en mi engine que hago yo solo (Evil Engine) uso singletons  :huh:  :blink:

deadLock++

 El Singleton se utiliza para asegurarnos que de una clase exista solamente una instancia en todo el sistema. Uno puede instanciar en una variable global determinada clase pero eso no evitaría que algún programador despistado cree una instancia local de dicho recurso lo cuál podría trae efectos secundarios no deseados (ej: en un logger borrar todo lo escrito hasta el momento).

Por otro lado realizar un GetInstance del Singleton no es muy caro si utilizas funciones inline, debido a que el código es "pegado" directamente en lugar de realizar la llamada a la función, es decir que acceder a un Singleton trae practicamente el mismo costo asociado a acceder al objeto por medio de una variable global.

Claro que esto no es así si en cada GetInstance se hace un new, cosa que sólo ocurre en el primer llamado, en mi motor realizo un GetInstance desde la clase Game, asegurandome que luego quien lo invoque, para obtener un puntero a dicho objeto, sólo conste del pasaje de la referencia.


tiutiu

 Joer, es q a ver, es poner el limite entre seguridad de programacion y eficiencia. Si piensas en hacer un motor q no tenga costes adicionales de llamadas pues deberas sacrificar q todo el mundo (incluso tu) haga alguna instancia o ejecute alguna funcion en algun sitio dnd no deberia.
Ya no solo por lo claro q queda el codigo (y si, bonito tb (ole)) sino por la seguridad d q crees 2 texture manager y q ya no sabes dnd tienes las texturas, o t sale una malla de tu barbaro-super-machote con su espadon y ves q la textura es la d lara croft con abrigo d piel, y en la espada sale una textura d un arbol.

Si vas a hacer el motor tu solo y confias en q no la cagaras pues entonces optimizalo lo q puedas ;)
b>:: Pandora's Box project ::
Notas e ideas sobre desarrollo de engines para juegos

tamat

 Pero a ver, SE PARA QUE ES UNA CLASE SINGLETON!!!, no necesito que me reciteis lo que dice el Design Patterns joer, si no supiera para que es una clase singleton no tendria 4 en mi engine, ¿no creeis? Lo digo por el post de DeadLock++... y tampoco necesito que me expliqueis lo que significa inline que lo tengo más que aprendido.

Volveré a formular la pregunta por tercera vez:
- ¿ Donde es más conveniente usar una clase singleton en lugar de un namespace con variables globales?

Porque que yo sepa vuestras explicaciones son tan utiles con uno como con otro metodo, en ambos casos de tener un sistema grande que utilizasen varias personas sería igual, en vez de acceder a la unica instancia de la clase singleton pues apelarian a una funcion global que hace uso de variables globales y que se encuentra en un .h aparte por lo que todos los programadores compartirian las mismas variables globales de dicho namespace, eliminando el problema de multiples instancias que todos conocemos y que tiutiu ha explicado.

Vale que el coste en ejecución de solicitar la instancia es minimo ya que si el metodo es inline no supone ningun coste adicional, pero sí que supone un coste adicional para el programador tener que preocuparse en buscar esa instancia.

Por cierto DeadLock++, tu metodo de hacer una clase singleton segun tengo entendido no es el más adecuado ya que una clase singleton se autoinstancía cuando no hay ninguna instancia previa y segun tú te estas preocupando de instanciarla al principio desde tu clase Game.

Para los que no lo sepan una clase singleton es así:

class MiClase
{
   private:
       MiClase();

   public:
   static MiClase* GetInstance()
   {
        static MiClase* instance = NULL;
        if (instance == NULL)
              instance = new MiClase();
        return instance;
   }
};


De este modo es imposible crear una instancia replicada ya que el constructor es privado y por lo tanto inaccesible, unicamente conseguiremos una instancia si usamos el metodo estatico GetInstace que nos garantiza que devolverá siempre la misma.
Por un stratos menos tenso

CoLSoN2

 tamat lee bien a deadLock porque dice lo mismo que tú xD
Manuel F. Lara
Descargar juegos indie  - blog sobre juegos indie y casual
El Desarrollo Personal.com  - blog sobre productividad, motivación y espíritu emprendedor

tamat

 Nops, no dice lo mismo que yo, lo que yo deduzco de su post es que él cree que yo pretendo guardar una instancia de mi clase en una variable global cuando no es eso, yo hablo de romper la clase singleton y dejar sus atributos como variables globales y sus metodos como funciones globales para que no exista modo alguno de que existan "varias instancias" (aunque esta palabra en C no tiene mucho significado). Algo parecido a lo que es el patron monostate.

Y sobre lo que comenta de su manera de hacer la clase singleton habla de que instancía manualmente la unica instancia de la clase cuando eso en una clase singleton tiene que ser automatico.
Por un stratos menos tenso

ethernet

 unreal usa variables globales y funciona perfectamente. Las usa para las cosas tipicas, como el log, el control de memoria y esas cosas.

Por otro lado no habeis oido hablar por ejemplo de los lazy loaders ? creo q el singleton es un metodo para implemenar algo asi

saludos ;**

tamat

 Es que precisamente todo esto viene en parte a que ultimamente me doy cuenta de que todos los engines (y APIs en general) usan namespaces para relegar funcionalidades del sistema y reservan lo puramente POO para cosas más tangibles como los elementos del escenario.
Y ahora veo lo util de eso solo que hasta ahora estaba algo cegado por el purismo de la POO y no me daba cuenta de que cuanto más encapsulas mas rompes el sistema y más te cuesta mantenerlo interconectado.
Por un stratos menos tenso

deadLock++

 
Cita de: "tamat"Y sobre lo que comenta de su manera de hacer la clase singleton habla de que instancía manualmente la unica instancia de la clase cuando eso en una clase singleton tiene que ser automatico.
Esta es mi implementación de Singleton:


template <class Type>
class Singleton
{
public:
   static Type * GetInstance(void)
   {
           if (!Singleton::m_pInstance)
                Singleton::m_pInstance = new Type;
   return Singleton::m_pInstance;
   }

   static Type & GetReference(void) { return *(Singleton::GetInstance()); }
 
protected:
Singleton() {}
        Singleton(const Singleton &) {}

        static Type * m_pInstance;
};


La idea es que cualquier clase que desee comportarse como Singleton sólo deberá declararse del siguiente modo:


class LoQueSea : public Singleton<LoQueSea>
{
   // ...
}


Por otro lado, tamat, no sé porque te tomas a mal lo que te dicen, ni yo ni los demás muchachos podemos saber que conoces y que no; si algo que te dicen ya lo sabes: ¡déjalo pasar! :) . Por ejemplo, cuando te referías al uso de variables globales yo no tenía idea de como lo hacías, supuse que en algún lado ponías un:

TextureManager gTextureManager;

lo cuál no impide que alguien equivocadamente dentro del código de alguna otra función se realice su propia instancia de TextureManger. Claro que esto también puede ser logrado por otros métodos, claro que puedes meter todas las funciones del TextureManager en un namespace y utilizar variables globales, a lo que yo hacía referencia con el uso de las inlines, es que no pagas un coste extra de "llamada a función" por obtener la referencia al Singleton y por otro lado ¿cómo podría saber yo si sabes o no sabes lo que es un inline?

También puedes crear una clase que sólo posea métodos estáticos que realicen las funciones que correspondan, esta clase no requeriría instanciación y su uso sería similar al uso de funciones globales dentro de un namespace. El asunto es que si no existe creación de objeto alguna, deberás verificar que las variables que utilice tu función hayan sido debidamente creadas.

Ej: Si tienes un Logger, un método de seguro será "Write" (o como desees llamarlo). En la construcción del objeto Logger, se abrirá el archivo de log y el método Write simplemente realizará la escritura correspondiente (para evitar que el Write abra, escriba y cierre en cada llamada). Invocar a Write sin construir el objeto sería imposible. De otro modo, si yo hubiese creado una función global OpenLog y otra WriteLog, nadie podría verficiar que se invoca el Open antes del Write, claro que tu me dirás que no crearás OpenLog sino que el Write mismo abrirá el archivo cuando verifique antes de escribir que el mismo no se encuentra abierto, con lo cuál tenemos instrucciones de código de chequeo extras a ejecutar en cada Write, que no tendrías en un objeto.

Y este sólo fue un ejemplo, si la inicialización de un objeto es un tanto mas compleja, el uso de funciones globales deberán verificar su correcta inicialización antes de cada uso.

Seguramente, tu me podrás decir el modo de rebatir esto, por como lo codificas o las precauciones que tienes al usarlo ¡y está bien! Con ningún patrón podrás hacer algo que no se pueda hacer con otros métodos convencionales que ni siquiera sean POO. De hecho, si alguien se presentase aquí y preguntara que ventajas trae C++ respecto a C, se podría armar un lindo y extenso debate, ya que si quien pregunta es un experto en C, podría ir rebatiendo todas las ventajas de C++ indicando como resuelve el mismo problema en C y evidenciando las desventajas del C++.

Saludos.

tamat

 Vale, primero perdir disculpas si me exalté un poco en mi post pero es que me da rabia que la gente asuma de antemano que no se nada de C++ y encasillen mi duda en el paquete de "dudas triviales de un novato" y encima trate de darme una lección de POO sin molestarse en presuponer que sí se y leer en produndidad mi duda. Prefiero que presupona que lo se todo y se explique a su modo y si alguna cosa no la entiendo ya me molestaré en buscar en el google.

Usar un template para las clases singleton lo conocía aunque reconozco que no lo utilizaba, weno, simplifica un poco la programación. Me apunto tu codigo aunque mi duda no va enfocada hacia como crear singletons ni estrategias para agilizar el uso sino a una comparacion entre la singleton y el namespace tradicional.

Tu ejemplo me parece adecuado ya que por primera vez en este thread se plantea una diferencia entre ambos y es que en el singleton podemos tener controlado el momento de la construccion mientras que en un namespace no, podria optar por la solucion que tu das de hacer un checkeo en cada funcion para comprobar si se han hecho las inicializaciones propias pero es un coste adicional como tu bien apuntas. Existe otra alternativa que es la que usan muchos APIs (SDL por ejemplo) y que consiste en obligar a efectuar la ejecución de una funcion de inicialización al principio de tu aplicación, en el caso de SDL es SDL_init() si no recuerdo mal, dentro de esa función se hacen todas las llamadas pertinentes para garantizar que el sistema estará preparado para su posterior uso y además sirve como funcion de testeo del sistema para comprobar que todo esté operativo. Tambien se podría asignar a un puntero global el resultado del GetInstance() de una clase singleton que podria llamarse Inicializador y en cuyo constructor se efectuen todas las inicializaciones pertinentes, de este modo nos ahorramos la inicializacion manual aunque yo prefiero hacerlo a mano para evitar problemas como cuando llamas a funciones de openGL antes de que el contexto de opengl sea creado.

Otra manera algo más compleja sería tener una clase con todos los miembros estaticos que tu comentas y que yo ya mencioné en un anterior post, se conocen como clases Monostate y a decir verdad en la practica es lo mismo ya que en vez de poner el nombre del namespace para cada llamada pues ponemos el nombre de la clase. Supongo que para los puristas de C++ esto es más correcto que no un namespace aunque yo lo veo algo sucio y algo me dice que tiene algun coste adicional.

Bien, quien da más?

Por un stratos menos tenso






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.