2010-03-02 9 views
6

Estoy escribiendo un motor de juego en Java para un juego de cartas de dos jugadores para el cual mis alumnos escribirán jugadores de inteligencia artificial.Cómo crear dos interfaces para una clase de Java, una de solo lectura, una de lectura y escritura?

Los jugadores de IA se turnarán para jugar a las cartas en su 'campo' como parte de la 'mesa' delante de ellos. Pueden atacar con una carta de su campo una carta en el campo del otro jugador. Las tarjetas pueden estar boca arriba o boca abajo.

La clase GameEngine le permite a un jugador AI hacer su turno llamando al método GamePlayer.TakeTurn (GameEngine spa). El jugador puede preguntarle al motor del juego por el campo del jugador defensor para que el jugador pueda tomar decisiones basadas en el número de cartas que hay y las que están boca arriba. Digamos que este método es GameEngine.GetDefendingField()

Ahora, quiero asegurarme de que el jugador atacante no puede modificar el campo del jugador defensor o las cartas en el campo del jugador defensor y que el jugador atacante solo puede identificar la cara arriba cartas en el campo de jugadores defensores.

Tengo clases para Card, Field (una ArrayList of Cards), GamePlayer y GameEngine. ¿Hay alguna forma de crear una interfaz para el Campo para que GameEngine pueda devolver el campo del jugador defensor sin que el jugador atacante pueda cambiarlo? y sin que el jugador atacante pueda refundir el campo del jugador defensor en algo que pueda cambiarse.

¿Puedo hacer que GameEngine.GetDefendingField() devuelva una interfaz de solo lectura a un campo que no puede ser refundido a un campo?

tia,

SH-

+2

@shindigo: ¿qué tan lejos quieres ir para "proteger" tu aplicación de tu estudiante? Si me permite ejecutar código en su JVM sin un administrador de seguridad instalado (que es el caso para el 99.9% de las aplicaciones de Java aquí), diablos, incluso puedo cambiar el contenido de inmutable, pero no realmente. objetos inmutables, como, por ejemplo, una Cadena. – SyntaxT3rr0r

Respuesta

11

Si desea lograr esto sin copiar y sin la posibilidad de convertir la referencia de interfaz de solo lectura a una interfaz de lectura-escritura correspondiente, podría intentar utilizar un enfoque de envoltura.

Para una interfaz de esta manera:

interface Info { 
    String getInfoA(); 
    void setInfoA(String infoA); 
    String getInfoB(); 
    void setInfoB(String infoB); 
} 

Se puede crear un envoltorio de sólo lectura que ignora los emisores o lanza excepciones, como:

class ReadonlyInfo implements Info { 
    final Info instance; 
    ReadonlyInfo(Info info) { 
     if (null == info) { throw new InvalidArgumentException(); } 
     instance = info; 
    } 
    String getInfoA() { return instance.getInfoA(); } 
    void setInfoA(String infoA) { /** silent ignore */ } 
    String getInfoB() { return instance.getInfoB(); } 
    void setInfoB(String infoA) { throw new IllegalStateException(); } 
} 

Al utilizar el enfoque de la envoltura puede utilizar el polimorfismo en Del lado del cliente, evite lanzar la referencia para obtener más derechos de acceso y elija entre ignorar silenciosamente a los emisores llamados o lanzar una excepción de acceso ilegal.

Nota: por supuesto no estará a salvo contra el código que utiliza la reflexión para recuperar el objeto envuelto.

+2

+1: Lo único que haría de manera diferente es lanzar UnsupportedOperationException (como las clases de Colección inmutables) en lugar de IllegalStateException. – Adamski

+0

Gracias, probaré esto esta noche y les contaré cómo fue. – shindigo

+0

Excluye UnsupportedOperationException y ** not ** IllegalStateException. Los dos indican condiciones bastante diferentes. –

0

no se puede definir una interfaz Java que hace cumplir acceso de sólo lectura a los campos/miembros ya que es un detalle de implementación definida en una clase. (De hecho, la forma de almacenar los datos internamente es un detalle de implementación)

Sugeriría crear dos implementaciones de su interfaz de campo, una con acceso de solo lectura y otra que tenga lectura/edición. Puede hacer que cada clase implemente la interfaz de campo, quizás con una clase AbstractField como clase principal de cada uno para cubrir la funcionalidad común.

A continuación, puede tener constructores para cada clase concreta (solo lectura y lectura/escritura) para convertir entre los dos, si es necesario.

2

Aquí está hablando Modelo-Vista-Controlador básico, con su Modelo siendo Tarjeta y Campo, el Controlador es GamePlayer y GameEngine, y la Vista aún no está definida. Su campo es su modelo, y le gustaría crear 2 interfaces diferentes que accederían al modelo de campo, una de solo lectura y una de lectura y escritura. Su implementación de lectura y escritura probablemente devuelva el objeto Field. Su implementación de solo lectura atravesaría los elementos en el campo y solo devolvería aquellos a los que tenían acceso, haciendo una copia profunda de cada tarjeta en el campo devuelto.

2

Me gusta la idea de usar seguridad de tipo y polimorfismo para controlar este tipo de control de acceso en tiempo de compilación, especialmente cuando se enseña.

Una buena forma de abordarlo sería tener dos interfaces de campo. Tal vez Field y MutableField, donde este último amplía el primero y agrega los métodos de cambio. Su FieldImpl podría, por supuesto, implementar ambos.

El problema, por supuesto, es que desea devolver diferentes tipos declarados (Campo o MutableField) de su motor de juego según el campo que esté utilizando.Si tiene un único modelo, puede usar algo como getMyField() y getOtherPlayerField() ya que de la descripción se desprende que al hacer una llamada a getField() uno sabe exactamente qué campo es el que quiere.

+0

Simplemente no coloque "I" delante de los nombres de la interfaz ya que este no es el estándar de codificación Java. Ver http://stackoverflow.com/questions/541912 –

+0

@Steve: Tienes razón. Mal hábito de demasiados años de programación de plugins de Eclipse. – Uri

+0

Nunca me di cuenta de que Eclipse pone una "I" delante de las interfaces. –

1

Puede crear una interfaz de solo lectura que solo tenga captadores. La extensión de esa interfaz sería una interfaz de lectura y escritura que agrega setters.

+0

Tenga en cuenta que esto no impide que el reproductor AI obtenga acceso a los campos simplemente al convertir el objeto en la interfaz de lectura-escritura. –

+1

Si la interfaz RW es una subclase de la interfaz RO, entonces no puede convertir un objeto RO al tipo RW. En Java, solo puede transmitir objetos a los tipos que implementan. – PanCrit

1

Definitivamente puede hacer esto. Es un enfoque llamado attenuation en la literatura de Capacidades de objeto. Siempre que el tipo restringido no se defina como un subtipo del tipo poderoso, el lanzamiento del objeto no proporcionará más autoridad.

0

Puede crear dos contratos, uno que permita tanto obtener como establecer mientras que el otro soporte solo obtener.

Ejemplo)

public interface Info { 
    void setA(String data); 
    String getA(); 
} 

public interface ReadableInfo { 
    String getA(); 
} 

public class ReadableInformation implements ReadableInfo { 
    private Info underlyingInfo; 

    public ReadableInformation(Info info) { 
    this.underlyingInfo = info; 
    } 

    @Override 
    public String getA() { 
    return underlyingInfo.getA(); 
    } 
} 

Con esto, se puede convertir un contrato mutable (Info) a uno que sólo es compatible con descriptores de acceso (ReadableInfo). Pero esto requiere entregar objetos de un tipo diferente (Info vs ReadableInfo) en función de quién sea el cliente.

Cuestiones relacionadas