2012-08-04 3 views
9

ho iniziato a giocare con oggetti di valore immutabili in Java mentre si lavora su un progetto di gioco, seguendo l'approccio "campi finali pubblici":Come estendere i tipi immutabili in Java

public class Team { 
    public final String name, flag; 

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

Questo funziona abbastanza bene per me finora, ma ho bisogno di diverse serie di informazioni aggiuntive sulla squadra in diverse circostanze. Ad esempio, una squadra ha un colore impostato durante una partita. La domanda è: qual è il modo migliore per gestire questi set di informazioni estese? So che questa è una domanda abbastanza generale, ma voglio continuare a usare oggetti immutabili e questo potrebbe influenzare la soluzione.

Ecco le opzioni che ho trovato. Molti di loro sono probabilmente "abbastanza buoni", ma mi piacerebbe imparare alcuni argomenti a favore e contro di loro per riferimenti futuri.

Opzione 1: Tutto in una classe

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; 
    } 
} 

Questo richiede solo una classe per tutti gli usi, ma non v'è alcuna indicazione di ciò che ci si aspetta i dati extra/forniti tipo-based.

Opzione 2: Subclassing

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; 
    } 
} 

Questo potrebbe rendere un contenuto a base di equals() implementazione impossibile.

Opzione 3: Composizione

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; 
    } 
} 

meno codice copia/boilerplate se i dati del team e dati aggiuntivi spesso cambiano in modo indipendente.

Opzione 4: Coppia/Tuple (utilizzando una classe Pair immutabile)

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 classe personalizzata che lega Team e TeamColor insieme.

tendo verso l'opzione 3 o 4, ma mi interessa le vostre opinioni, argomenti e sentimenti viscerali :)

+1

Oppure utilizzare * * interfacce (ad esempio 'ITeam',' ITeamColor') .. i costruttori non possono essere unificate in ogni caso. –

+2

Se tutte le tue squadre hanno un colore, dovrebbe essere tutto in una classe. – Strelok

+0

Non è davvero una risposta, ma direi che dovresti lasciare che il client (il codice che usa oggetti e colori 'Team') guidi il design dell'interfaccia. Cioè, scrivi prima il cliente, basta eliminare il modello. Quindi usa ciò che hai imparato per perfezionare il modello e completare la sua implementazione. – erickson

risposta

6

come hai detto. La squadra può apparire in diverse circostanze. Queste circostanze sono il contesto che fornisce al team gli attributi aggiuntivi.

Pertanto, suggerisco di utilizzare la composizione per ogni contesto diverso che aggiunge i dati.

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

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

Forse avrai:

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

Composizione suona come una buona opzione per l'aggiunta di dati di contesto che è necessario per essere mutevole.

In Java le classi immutabili sono in genere contrassegnate con final e non possono essere estese. Vedere String come esempio. Questo esclude l'opzione numero 2.

Stanco dell'uso di coppie. Ci sono molti buoni motivi per cui il tipo di coppia non è stato aggiunto a Java. In questo caso i tuoi dati vengono modellati meglio creando un nuovo tipo di dati (cioè attraverso la composizione).

migliori pratiche consigliate per la creazione di classi immutabili: http://www.javapractices.com/topic/TopicAction.do?Id=29

+0

Un buon punto, se pubblicizzi una classe come immutabile è probabilmente una buona idea assicurarsi che non ci possano essere sottoclassi mutabili, anche se in questo caso le uniche cose che potrebbero comportarsi inaspettatamente sono i metodi di Object.Ma sto facendo una domanda pedante, quindi mi merito delle risposte pedanti: P – Medo42

0

si potrebbe considerare mantenere questo pezzo di informazioni al di fuori dell'oggetto Team. Ad esempio, potresti avere una mappa o una mappa

In questo modo, potresti arricchire gli oggetti con molti valori aggiuntivi senza introdurre nuove classi o modificare le classi esistenti. Inoltre, manterrebbe la proprietà immutabile degli altri oggetti.

1

Se una classe immutabile è ereditabile, o se contiene aspetti i cui tipi potrebbero essere mutabili, allora non avrà alcuna sicurezza contro il danno. Allo stesso modo un'interfaccia "immutabile" non avrà alcuna sicurezza. Allo stesso modo anche se un oggetto ha membri di tipi estensibili, e l'oggetto è considerato come contenente una qualsiasi delle informazioni negli oggetti indicati da tali membri. Spetta a te decidere se questo è un problema. Le interfacce immutabili e le classi mutevoli estendibili possono essere molto più versatili di quelle profondamente sigillate; se una classe non sarà sigillata in profondità, c'è un piccolo vantaggio in termini di sicurezza nel renderla parzialmente tale.

noti che è possibile per un oggetto profondamente immutabile avere un campo che è di un tipo mutabile, se la semantica del campo specificato che identifica, anziché contiene, l'oggetto in questione. Ad esempio, può essere utile avere un oggetto "istantanea" che contiene riferimenti ad alcuni oggetti mutabili e, per ogni oggetto, una copia immutabile del suo stato corrente; l'oggetto "snapshot" esporrà quindi un metodo per ripristinare ogni oggetto allo stato che aveva quando è stato generato lo snapshot. L'oggetto dell'istantanea sarebbe "profondamente immutabile" nonostante il suo riferimento a un oggetto mutabile, dal momento che i riferimenti dell'istantanea agli oggetti mutabili esistono puramente per identificarli e le identità di tali oggetti non cambierebbero anche se il loro stato.

Uno schema che mi piace in .net, che dovrebbe funzionare anche in Java, è quello di avere interfacce come IReadableFoo e IImmutableFoo; quest'ultimo erediterà il primo ma non aggiungerà nuovi membri. L'interfaccia IReadableFoo dovrebbe contenere, oltre ai membri per leggere le sue proprietà, un membro AsImmutable() che restituirebbe un IImmutableFoo. Questo approccio consente al codice di utilizzare tipi mutabili quando è conveniente, riducendo al minimo la copia difensiva ridondante (il codice che vuole persistere qualcosa deve chiamare AsImmutable() su di esso, se l'oggetto in questione è modificabile, quell'azione creerà una copia immutabile, ma se l'oggetto è già immutabile, non sarà richiesta nessuna nuova copia).

0

Raccomando il motivo Decoratore.

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

Questo non è un decoratore - per questo, 'Team' dovrebbe essere un'interfaccia. Se passiamo all'utilizzo di getter e interfacce, il decoratore probabilmente combinerebbe i vantaggi della composizione (meno copia) con quelli dell'ereditarietà (può usare 'TeamWithColor' ovunque sia possibile usare' Team'). Sul lato negativo, devi mettere getter deleganti in 'TeamWithColor', quindi non puoi aggiungere campi a' Team' senza modificare 'TeamWithColor'. Inoltre, richiederebbe più codice (due interfacce, due classi, molti getters). – Medo42