2011-09-20 9 views
10

Estoy diseñando un motor de juegos en Java.Java: Imposición de objetos doblemente vinculados

En el núcleo de este motor existen las dos clases Asset y Attribute, donde un Asset tiene una lista de Atributos. La mayoría de los Atributos no necesitan ningún vínculo de respaldo a su Atributo, lo que significa que los Atributos pueden, y con frecuencia aparecen, en las listas de más de un Activo. Sin embargo, existe una extensión de atributo llamada UniqueAttribute, que es una implementación para aquellos que son específicos de su activo, y utilizan un enlace de regreso.

  1. Idealmente, el método de mi addAttribute activos sería algo como esto si corté el otro código:

    public void addAttribute(Attribute attribute){ 
        if(attribute instanceof UniqueAttribute) 
        ((UniqueAttribute)attribute).setAsset(this); 
        attributeList.add(attribute); 
    } 
    

    Por desgracia, ya que viven en diferentes paquetes, UniqueAttribute.setAsset() debe ser pública . Esto deja el método abierto a los usuarios externos del motor con el que meterse, y aunque pude simplemente decirlo usando este método directamente, es un error, parece bastante descuidado.

  2. La segunda opción es proporcionar la UniqueAttribute con el activo en la construcción, lo que significa que el código en el punto de la creación sería algo como esto:

    asset.addAttribute(new UniqueAttribute(asset)); 
    

    Mientras que puedo agregar un cheque-y- descartable o afirmar para confirmar que se transfiere el activo correcto, básicamente estoy confiando en que el usuario conecte los dos, lo que también preferiría no hacer.

  3. La tercera opción es morder la bala y poner 50 archivos de Java en el mismo paquete para que pueda utilizar la visibilidad estándar.

¿Hay algún tipo de patrón o algo que ayudará a unir estos dos juntos sin exponer los cables, o me obliga a poner todo en un solo paquete masivo?

Irrelevant rant: Siempre me ha disgustado que el concepto de subpaquetes en java no se haya ampliado realmente de manera significativa. Un subpaquete, en lo que respecta a Java, es simplemente un paquete diferente, y ha habido muchas ocasiones en que podría hacerlo con más modificadores de visibilidad directamente relacionados con esto.

+0

¿Tal vez la opción 2 con algún tipo de fábrica que conoce la relación entre los atributos únicos y los activos? –

+0

Aunque supongo que tener reglas especiales para subpaquetes podría haber funcionado, casi nunca es necesario (ya que puede usar sistemas como Spring u OSGi para forzar la separación entre interfaz e implementación) y habría hecho las cosas mucho más complejas por muy poca ganancia. Una implementación simple y confiable es más importante que una característica "linda" pero compleja. –

Respuesta

2

Mi sugerencia es que el Asset, Attribute y UniqueAttribute estén todos en el mismo paquete (posiblemente junto con algunas otras clases principales de "motor"). Luego puede usar la visibilidad del paquete estándar para UniqueAttribute.setAsset.

No necesita colocar todas las demás clases en el mismo paquete: su método Asset.addAttribute debe ser público y accesible desde otros paquetes para que el resto de la aplicación pueda usarlo directamente.

Por lo que la solución podría llamarse "3-" en su categorización.

Como algunos puntos más generales, también tenemos en cuenta:

  • si realmente necesita la complejidad de ambos atributos y UniqueAttributes - No estoy seguro de que realmente, después de haber puesto en marcha un modelo de objetos de juego bastante complejo y sin necesitando algo que pareciera un UniqueAttribute. Si el UniqueAttribute "necesita un enlace de vuelta", ¿entonces quizás está tratando de ser demasiado inteligente/hacer demasiado?
  • Incluso si necesita ambos, ¿realmente desea escribir un código que los trate de la misma manera/como parte de la misma jerarquía de objetos? parecen bastante conceptualmente diferentes, y terminará escribiendo mucho código condicional si combina los dos ...
  • Existen varias otras ventajas de los atributos que se comparten y son inmutables consistentemente: es mejor para el uso de la memoria, la concurrencia y la capacidad de prueba, entre otras cosas. Y como son presumiblemente bastante pequeños, el costo de la semántica de copiar sobre escritura es trivial en los casos en que lo necesite.
+0

Anteriormente estaba usando el método uno anterior, por lo que UniqueAttribute no es nuevo; solo estoy viendo algunas mejoras fundamentales. Su punto dos: creo que tendría demasiado código condicional si los separara, ya que hay muchos puntos donde la lista de Atributos se itera para un tipo o tipo específico. Sin embargo, es perspicaz; lo pensaré más. – Numeron

+0

¡Ah, entiendo por qué quieres los enlaces de la parte de atrás ahora! Si el problema es iterar sobre todos los objetos con un atributo específico, entonces quizás desee considerar mantener un índice separado (¿quizás HashSet?) De todos los objetos que tienen el atributo importante. Puede actualizar este conjunto cuando los objetos se agregan/eliminan o cuando se cambian los atributos. Aunque tiene que mantener este índice sincronizado, esto es eficiente, bastante fácil de probar y evita la necesidad de poner lógica compleja en los Atributos mismos. – mikera

0

Bueno, básicamente, que quiere hacer 3 cosas: método setAsset

  1. hacer visible dentro del paquete que contiene la clase de activo
  2. método de ocultar setAsset de todos los otros paquetes
  3. no utilizan sub-paquetes para lograr eso

Eso es un poco problemático: si declaras público ese método en la clase de Atributo todas las demás clases incluyendo th en el paquete (vamos a llamarlo AttributePackage), , no puede evitar que el usuario incluya ese paquete en algún lugar.

Por otro lado se podría hacer lo siguiente:

  1. crear una interfaz que contiene sólo el método atributo que el usuario debe utilizar, vamos a llamarlo AttributeInterface
  2. maquillaje Atributo aplicar esa interfaz
  3. añadir AttributeInterface a un nuevo paquete

Un usuario que desee utilizar la clase de atributo debe usarlo a través de AttributeInterface mientras tanto Asset usará la clase de atributo directamente para tener acceso a todos los métodos.

Voy a hacer un ejemplo:

//attribute interface package 
public interface AttributeInterface{ 
    public void publicAttributeMethodClientShouldUse(); 
} 

//attribute package 
public class Attribute{ 
    public void setAsset(Asset a); 
    public void publicAttributeMethodClientShouldUse(); 
} 

activos se referencia directamente por su parte Atributo de usuario deben hacer referencia a AttributeInterface. Espero ser claro.

+0

No estoy seguro de cómo el usuario podrá crear un Atributo con un enlace a su activo en esta instancia. La implementación de la interfaz significará que solo se trata de un atributo estándar sin acceso a getAsset(), y la extensión de la clase tiene el mismo problema que el que he presentado en la pregunta. – Numeron

2

me gustaría añadir un método de devolución de llamada en el atributo que se llama cuando se añade una instancia de atributo a un Activo:

class Attribute { 
    protected void addedToAsset(Asset asset) { 
     // do nothing 
    } 
} 

Este método se llama en el método addAttribute

class Asset { 
    public void addAttribute(Attribute attribute) { 
     attributeList.add(attribute); 
     attribute.addedToAsset(this); 
    } 

} 

Y el método se anularía en UniqueAttribute para controlar el enlace con el activo:

class UniqueAttribute extends Attribute { 
    Asset asset; 
    protected void addedToAsset(Asset asset) { 
     // manage the previous link if needed 
     if (this.asset != null) { ... } 
     this.asset = asset; 
    } 
} 

Con esta solución, Asset y Attribute deben colocarse en el mismo paquete. Pero UniqueAttribute podría estar en cualquier paquete que desee.

1

Modificar la opción scond

asset.addAttribute(new UniqueAttribute(asset)); 

así:

class UniqueAttribute { 
Asset asset; 
    public UniqueAttribute(Asset asset) { this.asset = asset; asset.addAttribute(this); } 
} 

Haz un enfoque similar para el atributo no único. Eso significa que en lugar de usar addAttribute() desde el exterior, solo utilícelo dentro de los constructores.

Otra opción es agregar dos métodos de fábrica a Asset: createAttribute() y createUniqueAttribute();

+0

De esta forma, el activo de UniqueAttribute también puede ser final, lo que reduciría la mutabilidad de UniqueAttributes. – OliverS

+0

Aunque esto significa que tengo el mismo problema anterior, excepto que estaré discutiendo con la visibilidad de addAttribute en lugar de setAsset. – Numeron

0

En primer lugar, estas entidades do parecen estar tan estrechamente relacionadas como para colocarlas en el mismo paquete. Los pondría en el mismo paquete y haría que el método addAttribute package-private.

Tenga en cuenta que desde la introducción de AccessibleObject en Java, la visibilidad y el control de acceso en el lenguaje se han vuelto simplemente cosméticos ... ¡Cualquiera que use su biblioteca puede tomar sus clases y hacer que los métodos y campos privados sean accesibles y modificables! Demonios, incluso pueden modificar final miembros! Por lo tanto, no ponga mucho énfasis en el aspecto de visibilidad y simplemente asegúrese de que el flujo de su modelo y método tenga sentido para sus usuarios y funcione correctamente.

+0

Disculpe, quise decir que el método 'UniqueAttribute.setAsset()' se hizo de paquete privado. – vagelis

Cuestiones relacionadas