2012-08-04 17 views
9

he empezado a jugar con objetos de valor inmutable en Java mientras se trabaja en un proyecto de juego, siguiendo el enfoque de "campos finales públicas":Cómo extender tipos inmutables en Java

public class Team { 
    public final String name, flag; 

    public Team(String name, String flag) { 
     this.name = name; 
     this.flag = flag; 
    } 
} 

Esto funciona bastante bien para mí hasta ahora, pero necesito diferentes conjuntos de información adicional sobre el equipo en diferentes circunstancias. Por ejemplo, un equipo tiene un color determinado durante un partido. La pregunta es, ¿cuál es la mejor manera de lidiar con estos conjuntos de información extendida? Sé que esta es una pregunta bastante general, pero quiero seguir usando objetos inmutables y eso podría influir en la solución.

Aquí están las opciones que se me han ocurrido. La mayoría de ellos probablemente sean "lo suficientemente buenos", pero me gustaría aprender algunos argumentos a favor y en contra de ellos para referencia futura.

Opción 1: Todo en una clase

public class Team { 
    public final String name, flag, colorName; 
    public final int colorRgb; 

    public Team(String name, String flag, String colorName, int colorRgb) { 
     this.name = name; 
     this.flag = flag; 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

Esto toma sólo una clase para todos los usos, pero no hay ninguna indicación basada en el tipo de lo que se espera de datos extra/proporcionada.

Opción 2: La subclasificación

public class TeamWithColor extends Team { 
    public final String colorName; 
    public final int colorRgb; 

    public Team(String name, String flag, String colorName, int colorRgb) { 
     super(name, flag); 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

Esto podría hacer que A es igual basadas en el contenido() aplicación imposible.

Opción 3: Composición

public class TeamWithColor { 
    public final Team team; 
    public final String colorName; 
    public final int colorRgb; 

    public Team(Team team, String colorName, int colorRgb) { 
     this.team = team; 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

menos código copiado/repetitivo si los datos del equipo y los datos adicionales a menudo cambian de forma independiente.

Opción 4: Par/Tupla (usando una clase Par inmutable)

public class TeamColor { 
    public final String colorName; 
    public final int colorRgb; 

    public Team(String colorName, int colorRgb) { 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

Pair<Team, TeamColor> teamWithColor = Pair.create(team, teamColor); 

... o con una clase personalizada que Curiosidades y TeamColor juntos.

que tienden a la opción 3 o 4, pero estoy interesado en sus opiniones, argumentos y sentimientos viscerales :)

+1

O utilizar las interfaces * * (por ejemplo 'ITeam',' ITeamColor') .. los constructores no se pueden unificar todos modos. –

+2

Si todos sus equipos tienen un color, entonces todo debería estar en una clase. – Strelok

+0

No es realmente una respuesta, pero yo diría que debe dejar que el cliente (el código que está utilizando los objetos y colores de 'Equipo') controle el diseño de la interfaz. Es decir, escriba primero al cliente, simplemente anulando el modelo. Luego, use lo que aprendió para refinar el modelo y finalizar su implementación. – erickson

Respuesta

6

como usted ha dicho. El equipo puede aparecer en diferentes circunstancias. Estas circunstancias son el contexto que le da al equipo los atributos adicionales.

Por lo tanto, sugiero usar composición para cada contexto diferente que agregue datos.

public class TeamWithColor { 
    public final Team team; 
    public final TeamColor teamColor; 

    public Team(Team team, TeamColor teamColor) { 
     this.team = team; 
     this.teamColor = teamColor; 
    } 
} 

Quizás tengas:

public class TeamDuringOlimpics{ 
    public final Team team; 
    public final TeamColor teamColor; 
    public final TeamFlag teamFlag; 

    public Team(Team team, TeamColor teamColor, TeamFlag teamFlagTeamFlag teamFlag) { 
     this.team = team; 
     this.teamColor = teamColor; 
     this.teamFlag = teamFlag; 
    }  
} 
2

Composición suena como una buena opción para agregar datos contextuales que se requiere para ser mutable.

En Java, las clases inmutables generalmente se marcan como final y no se pueden extender. Ver String como ejemplo. Eso descarta la opción número 2.

Se cansará de usar Pares. Hay muchas buenas razones por las que el tipo Pair no se ha agregado a Java. En este caso, sus datos se modelan mejor creando un nuevo tipo de datos (es decir, a través de la composición).

recomendados mejores prácticas para la creación de clases inmutables: http://www.javapractices.com/topic/TopicAction.do?Id=29

+0

Un buen punto, si anuncia una clase como inmutable, probablemente sea una buena idea asegurarse de que no pueda haber subclases mutables, aunque en este caso las únicas cosas que podrían comportarse inesperadamente son los métodos de Object.Pero estoy haciendo una pregunta pedante, así que merezco respuestas pedantes: P – Medo42

0

usted podría considerar mantener esta pieza de información fuera del objeto de equipo. Por ejemplo, podría tener un Mapa o Mapa

De esta manera, podría enriquecer sus objetos con muchos valores adicionales sin introducir nuevas clases o modificar clases existentes. Además, mantendría la propiedad inmutable de los otros objetos.

1

Si una clase inmutable es heredable, o si contiene aspectos cuyos tipos podrían ser mutables, entonces no tendrá seguridad contra la malicia. Una interfaz "inmutable" tampoco tendrá seguridad. Del mismo modo, también si un objeto tiene miembros de tipos extensibles, y se considera que el objeto contiene información de los objetos a los que hacen referencia esos miembros. Depende de usted decidir si eso es o no un problema. Las interfaces inmutables y las clases mutables extensibles pueden ser mucho más versátiles que las profundamente selladas; si una clase no va a estar profundamente sellada, hay pocas ventajas de seguridad para que sea parcialmente así.

en cuenta que es posible que un objeto profundamente inmutable tener un campo que es de un tipo mutable, si la semántica del campo especifica que identifica, en lugar de contiene, el objeto en cuestión. Por ejemplo, puede ser útil tener un objeto "snapshot" que contenga referencias a algunos objetos mutables y, para cada objeto, una copia inmutable de su estado actual; el objeto "instantánea" expondría un método para restaurar cada objeto al estado que tenía cuando se generó la instantánea. El objeto de instantánea sería "profundamente inmutable" a pesar de su referencia a un objeto mutable, ya que las referencias de la instantánea a los objetos mutables existen únicamente para identificarlos, y las identidades de esos objetos no cambiarían aunque su estado sí lo haga.

Un patrón que me gusta en .net, que también debería funcionar en Java, es tener interfaces como IReadableFoo y IImmutableFoo; este último heredaría el primero pero no agregaría ningún miembro nuevo. La interfaz IReadableFoo debe contener, además de los miembros para leer sus propiedades, un miembro AsImmutable() que devolverá un IImmutableFoo. Este enfoque permite que el código use tipos mutables cuando sea conveniente, mientras minimiza la copia defensiva redundante (el código que desea persistir debe llamar al AsImmutable(); si el objeto en cuestión es mutable, esa acción creará una copia inmutable, pero si el objeto es ya inmutable, no se requerirá una copia nueva).

0

Recomiendo el patrón Decorator.

public class TeamWithColor extends Team { 
    public final Team team; 
    public final String colorName; 
    public final int colorRgb; 

    public Team(Team team, String colorName, int colorRgb) { 
     this.team = team; 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

http://en.wikipedia.org/wiki/Decorator_pattern

+1

Esto no es un decorador, para eso, 'Team' tendría que ser una interfaz. Si cambiamos al uso de getters e interfaces, el decorador probablemente combinaría las ventajas de la composición (menos copia) con las de herencia (se puede usar 'TeamWithColor' en todas partes, se puede usar' Team'). En el lado negativo, tienes que poner getters de delegación en 'TeamWithColor', por lo que no puedes agregar campos a' Team' sin editar 'TeamWithColor'. Además, requeriría más código (dos interfaces, dos clases, muchos buscadores). – Medo42

Cuestiones relacionadas