Pregunta

Digamos que yo soy el encargado de la codificación de algún tipo de un RPG.Esto significa que, por ejemplo, voy desea realizar un seguimiento de un Character GameCharacter y estadísticas de los mismos, como la inteligencia, los bonus de daño o de puntos de vida.

Estoy positivamente miedo de que al final del proyecto puede terminar con la manipulación con un alto número de campos - y para cada uno tendría que asegurarse de que cumplan un conjunto muy similar de restricción y los comportamientos (por ejemplo, quiero ser acotada entre un mínimo y un máximo;Quiero ser capaz de distinguir entre un "valor base" y un "temporal"bono;Quiero ser capaz de incremento y decremento tanto sin pasar a través de un setters y getters).De repente, para cada campo que iba a necesitar una (dos?) getter y cuatro incubadoras y tal vez un par de resetters demasiado!Incluso por 10 campos que significa una gran cantidad de métodos a todos por igual, eek.

Para la Sequedad he empezado a encapsular la lógica de joder con esas estadísticas en Field las clases, de modo que yo podría escribir código como intelligence.applyBonus(10) o hitpoints.get() (que ocupa el valor devuelto está en su rango), etc.Incluso he ido a una longitud de crear clases para agrupar los campos juntos, pero ese no es el punto ahora.

Ahora, me golpeó en este problema, mientras que "enchufar" Field en GameCharacter:la mayoría de Java libros de texto dicen que cada clase debe tener campos privados con los públicos getters y setters.Lo que suena bien en teoría, y ya he construido toda una clase alrededor de un int;sin embargo, la idea no suena tan sólido cuando llames a un getter para obtener...un getter:

thisCharacter.getIntelligence().get() //eeek

Me gustaría mucho más tener acceso al campo directamente.Tal vez es mi Python/VB [1] "de fondo", pero para mí es más limpia, más clara y más sencilla:

thisCharacter.intelligence.get()

El (teórico) el problema con los campos públicos es que me estoy dando todo el control sobre ella;por ejemplo en algún otro punto en la base de código, por desgracia, los siguientes pueden ocurrir:

thisCharacter.intelligence = somethingThatReallyIsNull;

Suena como un error sutil...pero...Quiero decir, ¿debo realmente preocuparse por eso?Yo nunca va a asignar el Field directamente [2], me han documentado en el Javadoc de que esto no es algo que se debe hacer, pero todavía soy nuevo aquí, así que estoy un poco roto.

Así que me gustaría escuchar lo que su opinión sobre este tema.Son las ventajas de la encapsulación tan masiva que debo seguir adelante y tener getter los métodos getter y setter de captadores y así sucesivamente...o debo tomar la encapsulación en saludable medidas y salir de la Field como public en el campo?


[1] Sí, lo sé.He estado tratando de olvidar.Pero hemos visto recientemente también se un poco de C# y el hombre, no son propiedades dulce.Oh, bueno.

[2] excepto en los Constructores!Y un getter no me salve de una defectuosa constructor.

¿Fue útil?

Solución

Mi experiencia es que en situaciones en las que se necesita un mucho de los campos, el número, la naturaleza, denominación, y tipos de campos son tan flexibles y pueden cambiar durante la vida útil de su proyecto que se probablemente necesitará algún tipo de mapa en lugar de campos.

Por ejemplo tener un mapa de atributos de claves a valores.

Proporcionar llamadas públicas para obtener y establecer los atributos, pero no permita que todas las personas los utilizan (o asegurarse de que no lo hacen). En su lugar, crear clases para representar cada atributo que interesa, y que clase proporciona todas las funciones para la manipulación de ese atributo. Por ejemplo, si usted tiene la fuerza, usted podría tener una clase "StrengthManipulation" que se inicializa a un objeto determinado jugador, y luego proporciona captadores, emisores (todos con la validación y excepciones apropiadas), y tal vez cosas como el cálculo de la fuerza con bonos, etc. .

Una ventaja de esto es que desacoplar el uso de sus atributos de su clase de jugador. Así que si usted añada el atributo de inteligencia, no tiene que hacer frente y volver a compilar todo lo que manipula única fuerza.

En cuanto a acceder a los campos directamente, que es una mala idea. Cuando se accede a un campo en VB (al menos en los viejos VBS), que por lo general llama a una propiedad de captador y colocador y VB simplemente oculta la llamada () para usted. Mi opinión es que hay que adaptarse a las convenciones del lenguaje que está utilizando. En C, C ++, Java y similares que tienen campos y tiene métodos. Llamar a un método siempre debe tener la () para que quede claro que se trata de una llamada y otras cosas puede suceder (por ejemplo, se puede obtener una excepción). De cualquier manera, una de las ventajas de Java es su sintaxis más precisa y estilo.

VB para Java o C ++ es como mensajes de texto a la universidad la redacción científica.

Por cierto: Algunas investigaciones muestran que la facilidad de uso que es mejor no tener parámetros a los constructores y en lugar de construir y llamar a todos los emisores, si los necesita.

Otros consejos

Parece que estás pensando en términos de if(player.dexterity > monster.dexterity) attacker = player. Es necesario pensar más como if(player.quickerThan(monster)) monster.suffersAttackFrom(player.getCurrentWeapon()). No perder el tiempo con las estadísticas desnudos, expresan su intención real y luego diseñar sus clases alrededor de lo que se supone que deben hacer. Las estadísticas son una salida fácil de todos modos; lo que realmente importa es si un jugador puede o no puede hacer algún tipo de acción, o su habilidad / capacidad frente a alguna referencia. Pensar en términos de "es el personaje del jugador lo suficientemente fuerte" (player.canOpen(trapDoor)) en lugar de "lo hace el personaje tiene al menos 50 fuerza".

Steve Yegge tenía una muy interesante (si es muy largo) blog que cubre estos temas: El diseño universal patrón .

A mí parece que 'thisCharacter' bien podría tener un objeto 'inteligencia' para hacer frente a la inteligencia detrás de las escenas, pero me pregunto si eso debería ser pública en absoluto. Usted sólo debe exponer thisCharacter.applyInt y thisCharacter.getInt en lugar del objeto que se ocupa de él. No te exponga a su puesta en práctica por el estilo.

Mantenga sus campos privada! Nunca se desea exponer demasiado de su API. Siempre se puede hacer algo que era público y privado, pero no a la inversa, en futuras versiones.

Piense como si usted va a hacer eso en la próxima MMORPG. Usted tendría mucho margen para los errores, errores y mal innecesario. Asegúrese de propiedades inmutables son finales.

Piense en un reproductor de DVD, con su interfaz miniamilistic (play, stop, menú), y sin embargo tal detalle técnico dentro. Usted querrá ocultar todo lo que no vital en su programa.

Parece que su queja principal no es tanto con la abstracción de los métodos setter / getter, pero con la sintaxis del lenguaje para el uso de ellos. Es decir, que preferiría algo así como propiedades de estilo C #.

Si este es el caso, entonces el lenguaje Java tiene relativamente poco que ofrecer. Acceso directa sobre el terreno está muy bien, hasta que tenga que cambiar a un captador o colocador, y luego va a tener un poco de refactorización que hacer (posiblemente bien, si el control de toda la base de código).

Por supuesto, si la plataforma Java es un requisito, pero el idioma no es entonces hay otras alternativas. Scala tiene una sintaxis de la propiedad muy agradable, por ejemplo, junto con muchas otras características que podrían ser útiles para un proyecto de este tipo. Y lo mejor de todo, se ejecuta en la JVM, por lo que recibe el mismo portabilidad que se obtendría por escrito en el lenguaje Java. :)

Lo que parecen tener aquí hay una sola capa de un modelo compuesto.

Es posible que desee agregar métodos que añaden abstracciones al modelo, en lugar de tener que como un conjunto de modelos de nivel inferior.

Los campos deben ser definitiva, por lo que incluso si lo hizo hacerlos públicos no podían asignar accidentalmente null a ellos.

El "llegar" prefijo está presente para todos los captadores, por lo que es probablemente más el aspecto inicial de un nuevo problema como tal.

  

son las ventajas de la encapsulación de tal magnitud que debería seguir adelante y tener getters getter y setter captadores y así sucesivamente ... o debería tomar medidas saludables en la encapsulación y dejar el campo como un campo público?

OMI, la encapsulación no tiene nada que ver con envolver un captador / definidor alrededor de un campo privado. En pequeñas dosis, o al escribir bibliotecas de uso general, la desventaja es aceptable. Sin embargo, cuando no se controla en un sistema como el que usted describe, es un un antipatrón .

El problema con getters / setters es que crean demasiado apretado acoplamiento entre el objeto con los métodos y el resto del sistema.

Una de las ventajas de la encapsulación verdadera es que reduce la necesidad de captadores y definidores, desacoplar el objeto del resto del sistema en el proceso.

En lugar de exponer la implementación de GameCharacter con setIntelligence, por qué no dar GameCharacter una interfaz que refleja mejor su papel en el sistema de juego?

Por ejemplo, en lugar de:

// pseudo-encapsulation anti-pattern
public class GameCharacter
{
  private Intelligence intelligence;

  public Intelligence getIntelligence()
  {
    return intelligence
  }

  public void setIntelligence(Intelligence intelligence)
  {
    this.intelligence = intelligence;
  }
}

¿por qué no intentar esto:?

// better encapsulation
public class GameCharacter
{
  public void grabObject(GameObject object)
  {
    // TODO update intelligence, etc.
  }

  public int getIntelligence()
  {
    // TODO
  }
}

o incluso mejor:

// still better
public interface GameCharacter
{
  public void grabObject(GameObject object); // might update intelligence
  public int getIntelligence();
}

public class Ogre implements GameCharacter
{
  // TODO: never increases intelligence after grabbing objects
}

En otras palabras, un GameCharacter puede agarrar GameObjects. El efecto de cada GameCharacter agarrando la misma GameObject puede (y debe) varían, pero los detalles se encapsulan completamente dentro de cada aplicación GameCharacter.

Nótese cómo el GameCharacter está ahora a cargo de manejar su propia actualización de la inteligencia (intervalo de comprobación, etc.), lo cual puede suceder ya que agarra GameObjects, por ejemplo. El colocador (y las complicaciones se nota con tenerlo) han desaparecido. Es posible que pueda prescindir del método getIntelligence por completo, dependiendo de la situación. Allen Holub lleva esta idea a su conclusión lógica , pero, pero ese enfoque no parece ser muy común.

Además de la Uri de la respuesta (que yo apoyo totalmente), me gustaría sugerir que usted considere la posibilidad de definir el atributo de mapa de datos.Esto hará que tu programa es MUY flexible y se tendrá en cuenta una gran cantidad de código que no se dan cuenta de que no es necesario.

Por ejemplo, un atributo puede saber qué campo se une en la pantalla, ¿qué campo se une en la base de datos, una lista de acciones a ejecutar cuando el atributo de cambios (volver a calcular el% de aciertos podría aplicar a la fuerza y la dex...)

Haciéndolo de esta manera, usted NO tiene el código de cada atributo para escribir una clase a la DB o mostrarlo en la pantalla.Simplemente iterar sobre los atributos y utilizar la información almacenada en su interior.

Lo mismo se puede aplicar a las habilidades, de hecho, atributos y habilidades probablemente se derivan de la misma clase base.

Después de recorrer este camino, es probable que se encuentre con un muy serio archivo de texto para definir los atributos y habilidades, pero la adición de una nueva habilidad sería tan fácil como:

Skill: Weaving
  DBName: BasketWeavingSkill
  DisplayLocation: 102, 20  #using coordinates probably isn't the best idea.
  Requires: Int=8
  Requires: Dex=12
  MaxLevel=50

En algún punto, la adición de una habilidad como esto requeriría ningún cambio de código alguno, podría realizarse en bases de datos muy fácilmente, y TODOS los datos se almacenan en una sola habilidad objeto conectado a una clase.Usted podría, por supuesto, definir acciones, de la misma manera:

Action: Weave Basket
  text: You attempt to weave a basket from straw
  materials: Straw
  case Weaving < 1
    test: You don't have the skill!
  case Weaving < 10
    text: You make a lame basket
    subtract 10 straw
    create basket value 8
    improve skill weaving 1%
  case Weaving < 40
    text: You make a decent basket
    subtract 10 straw
    create basket value 30
    improve skill weaving 0.1%
  case Weaving < 50
    text: You make an awesome basket!
    subtract 10 straw
    create basket value 100
    improve skill weaving 0.01%
  case Weaving = 50
    text: OMG, you made the basket of the gods!
    subtract 10 straw
    create basket value 1000

Aunque este ejemplo es bastante avanzado, usted debería ser capaz de visualizar cómo se llevaría a cabo con absolutamente ningún código.Imagínese lo difícil que sería hacer algo como esto sin código si sus "atributos y habilidades" eran en realidad miembros de variables.

Considere el uso de un marco de "modelado", como EMF Eclipse. Esto implica definir sus objetos usando algo así como editor de Eclipse EMF, o en XML sin formato, o en Rational Rose. No es tan difícil como parece. Se define atributos, restricciones, etc. Entonces el marco genera el código para usted. Se añade etiquetas @generated a partes Añadió como getter y setter métodos. Puede personalizar el código, y luego editar ya sea a mano o por medio de alguna interfaz gráfica de usuario, y regenerar los archivos de Java.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top