2010-04-23 11 views
10

He estado programando una versión de software de un juego de mesa. Hasta ahora he escrito las clases que corresponderán a objetos físicos en el tablero de juego. Estoy bien en la escritura de la lógica del programa, sin embargo, he encontrado que muchas de las clases de lógica requieren acceso a los mismos objetos.Buen estilo de programación al manejar objetos múltiples

Al principio estaba pasando los objetos apropiados a los métodos como se los llamaba, pero esto se volvía muy tedioso, particularmente cuando los métodos requerían muchos objetos para realizar sus tareas. Para resolver esto, creé una clase que inicializa y almacena todos los objetos que necesito. Esto me permite acceder a un objeto de cualquier clase llamando a Assets.dice(), por ejemplo.

Pero ahora que lo he pensado, esto no parece correcto. Es por eso que estoy aquí, me temo que he creado algún tipo de clase de Dios. ¿Este miedo es infundado, o he creado una receta para el desastre?

Respuesta

7

Básicamente te has topado con el patrón singleton. En su mayor parte, es un mal patrón. Al permitir que cualquier parte de tu aplicación acceda a datos esencialmente globales como este en casi cualquier momento, terminas con un lío de spaghetti de código que es difícil de mantener, depurar y, sobre todo, probar.

Creo que es mejor crear un "Contexto", que contenga los dados, piezas, etc. actuales, y pasar el contexto según sea necesario a los métodos/clases que necesiten usarlo. Esto es mucho más limpio, aunque sí es un dolor tener que pasarlo a todas partes. Pero usted gana la ventaja de que puede hacer un seguimiento de quién está accediendo al Contexto cuando, y también puede crear Contextos simulados para fines de prueba. Si transfiere el contexto a un objeto de alto nivel y debe pasarlo a sus subcomponentes, sabrá de principio a fin cuál es el contexto y de dónde proviene.

Además, lo ideal es que el contexto sea inmutable. Eso puede no ser posible. Pero si para cada turno dado puedes crear un nuevo Contexto que capture el estado actual y sea inmutable, reduces aún más la sorpresa de tu aplicación.

+0

Esto no es realmente un Singleton, ya que el acceso se realiza a través de interfaces estáticas. Pero sufre muchas de las mismas desventajas, así que me abstendré de marcar. – DJClayworth

+0

Gracias Matt. Cambié el código para que solo el archivo de lógica principal creara y tuviera acceso directo al objeto GameAssets. Esto luego se pasa a otras clases/métodos. Sin embargo, todavía no parece correcto, tal vez porque mi lógica es de arriba hacia abajo en el diseño. Termino pasando este objeto por todo el lugar. Pero tal vez esa sea otra pregunta en sí misma: ¿cómo se divide la lógica del juego en pequeños fragmentos? Tendré que pensarlo. – Glitch

0

¿Es realmente una clase "dios", o simplemente un "contexto" que es un "contenedor" para todas las instancias de objetos interrelacionados de un juego que luego pasa a las diferentes llamadas a métodos (para que un argumento)? Este último es bastante común y no veo nada de malo en él, pero en este caso el contenedor en sí no tiene una funcionalidad real.

0

Gracias por hacer esta pregunta. Me he estado preguntando sobre el mismo problema por algún tiempo ahora. Pasar objetos a través del método y mantener los parámetros limitados requiere la creación de clases que puedan contener todos estos objetos.

Todavía creo que el diseño no es malo, ya que necesita una clase de dominio que pase por varias capas. Si esta clase de dominio no lleva objetos necesarios y no realiza ninguna lógica, debería estar bien.

2

Parece que está preguntando acerca de una filosofía general de Programación Orientada a Objetos. En general, encontrará que modelar objetos del mundo real para las clases no es siempre que tenga el mejor sentido para su código.

Una de las directrices que ha ayudado a calcular esta materia hacia fuera es un diálogo entre dos clases antropomórficas (si alguien sabe la fuente original de esta cita, lo agradecería un enlace!):

Clase A dice a Clase B: "Dame el valor de x".

Clase B: "¿Por qué quieres el valor de x?"

Clase A: 'Por lo tanto, puede que la brida'

Clase B: '. Me pregunta, y voy a la brida por usted'

Esto ayuda a pasar el mensaje que una clase está destinado a encapsular los datos y realizar manipulaciones en él. en general, se trata de una parábola que ha ayudado a organizar mi código mejor.

Otra cosa que puede que desee ver en son algunos Design Patterns orientada a objetos comunes Algo así como dados de juego m Ocho tiene más sentido como Singleton, ya que no necesita más de una instancia de la misma.

Si desea una buena introducción a los patrones de diseño, le recomiendo que tome el excelente libro Head First Design Patterns.

+0

La pregunta sobre quién debe flanquear B's x es más complicada de lo que ha indicado, y en el corazón del buen diseño. No es un hecho que B debería rebordear sus propios datos. Por ejemplo: ¿quién quiere el resultado? ¿Y la clase C rebordea sus propios datos de la misma manera? – DJClayworth

+0

Tiene toda la razón, pero todavía creo que es un truco útil pensar en la encapsulación. – Skrud

+0

+1 ... Algunas personas han ido mucho más allá y argumentan que la presencia de * getters * demuestra que no estás haciendo OO.Casi * todo el mundo *, * en todas partes * está usando getters, por lo tanto las personas estarán violentamente en desacuerdo (porque a las personas no les gusta cuando sienten que las "mejores prácticas" que están siguiendo desde hace 15 años son "atacadas"). Sin embargo, es un pensamiento fascinante. Cuando leo sobre esto, en lugar de pensar * "tonto, ** yo uso getters todo el tiempo, ¡sé que los getters son todos buenos!" * Pensé * "oh, eso es interesante" *:) – SyntaxT3rr0r

0

Tener ese tipo de clase de contexto que tiene acceso a todo es muy similar a tener variables globales. Se aplican los mismos inconvenientes. Una variable global se puede leer y cambiar por cualquier método. Esto combina todo lo que usa la variable global entre sí. El acoplamiento es malo porque cuando las cosas están acopladas, un cambio en un objeto puede causar algo en el otro objeto. Cuando el grado de acoplamiento aumenta, se vuelve muy difícil manejar la complejidad (una mariposa aleteando sus alas en nuestra clase de jugador puede causar una excepción en tu clase de dados). El cambio y el desarrollo adicional se vuelven más difíciles. Es difícil de detectar y los errores oscuros se vuelven inevitables.

Entonces, por ejemplo, un día mientras prueba su aplicación, puede notar que su objeto de dados está actuando de forma extraña. Llamas a dice.roll() y ves que devuelve 1 a veces. Pero eso no es posible en este caso porque tu jugador está lanzando dos de ellos. Debug y de alguna manera notan que la propiedad numberOfDice se cambió a 1 en algún punto. Con un método de contexto como el suyo, no será fácil encontrar quién cambió el número de Dice a 1 porque todos tienen acceso a los dados a través de su objeto de contexto. ¿Tú entiendes?

¿Cuál es la solución? Una de las metáforas que me gusta de la programación OO es dividir y conquistar. Necesita encontrar comportamientos, procesos, objetos que puedan aislarse entre sí. Debe dividir su problema en partes manejables que puedan aislarse del resto de las cosas que están sucediendo en su aplicación.

Aprender a hacer esto no es fácil, por supuesto, requiere mucho estudio, lectura, pensamiento, discusión y, por supuesto, codificación.

Cuestiones relacionadas