2010-06-25 7 views
10

Solitamente JavaPractices.com è un buon sito con una buona idea, ma questo mi disturba: JavaBeans are bad.I bean Java sono classi errate come classi di archiviazione dei dati?

L'articolo cita diversi motivi, principalmente il termine JavaBean significa "Un Java Bean è un componente software riutilizzabile che può essere manipolato visivamente in uno strumento di creazione". non archiviazione dei dati, viola alcuni schemi ed è più complesso.

Ora posso essere d'accordo con l'ultimo, ma ai miei occhi JavaBeans in una lista ha molto più senso di Maps nidificati. L'articolo afferma che i framework di mapping del database dovrebbero chiamare i costruttori, non i metodi set * e l'oggetto dovrebbe essere immutabile. Nella mia mente però, chiamando impostare * metodi quando si cerca di costruire un oggetto è più facile da leggere rispetto new MappedObject("column1", "column2", "yet another column", "this is stupid");

Io uso anche la classe di stile JavaBeans per altre cose oltre la mappatura di database, ad esempio per un bot IRC, avendo un oggetto per utente che viene aggiornato con varie cose. Non voglio creare un nuovo oggetto ogni volta che vengono date nuove informazioni, voglio aggiungerlo a uno esistente.

Quindi la mia domanda: utilizzare JavaBeans per l'archiviazione dei dati è una cattiva pratica e dovrebbe essere evitato, o è perfettamente sicuro?

+10

JavaBeans non rappresenta tanto un modello come un intero linguaggio anti-pattern. –

+3

puoi utilizzare un builder per ottenere i vantaggi dell'immutabilità ed evitare il costruttore super lungo. –

+6

Considerare l'apprendimento del Pattern Builder. http://en.wikipedia.org/wiki/Builder_pattern. – CoolBeans

risposta

19

Sembra che si stia interpretando erroneamente il testo.

ora posso essere d'accordo con l'ultimo, ma in JavaBeans del mio occhio in un elenco che rende molto più senso di mappe nidificate

Il testo non menziona mai cartine nidificati in alternativa (yiack)

... dovrebbe chiamare i costruttori, non impostare * metodi, e l'oggetto deve essere immutabile

Questa è una buona pratica, particolarmente utile quando si tratta di thread.

Ma non possiamo dire che utilizzando incastonatori è baaad sia, specialmente quando un singolo filo utilizza l'oggetto. Questo è perfettamente al sicuro.

Non voglio creare un nuovo oggetto ogni volta che vengono fornite nuove informazioni, voglio aggiungerlo a uno esistente.

va bene, fino a quando è possibile controllare l'oggetto non v'è alcun problema con questo, qualche altro può trovare più facile creare solo un nuovo oggetto.

L'utilizzo di JavaBeans per l'archiviazione dei dati è una pratica scorretta e deve essere evitato oppure è perfettamente sicuro?

No, non è una cattiva pratica. Non è perfettamente al sicuro anche. Dipende dalla situazione.

Il problema con oggetti mutabili (non con JavaBeans di per sé) utilizza thread diversi per accedervi.

È necessario sincronizzare l'accesso per evitare che un thread modifichi l'oggetto mentre altri lo accedono.

Gli oggetti immutabili non hanno questo problema, perché ... beh non possono essere modificati e, quindi, non è necessario sincronizzare nulla.

Per assicurarsi che un oggetto sia immutabile, è necessario dichiarare i propri attributi come finali.

class MyBean { 
    private final int i; 
} 

Se si desidera assegnare un valore ragionevole MyBean.i è necessario specificare nel costruttore:

public MyBean(int i) { 
    this.i = i; 
} 

Poiché la variabile è definitiva, non è possibile utilizzare un setter. Puoi solo fornire un getter.

Questo è perfettamente thread-safe e la cosa migliore è, non c'è bisogno di sincronizzare l'accesso, perché se due thread cercano di ottenere il valore di i entrambi vedranno sempre il valore che è stato assegnato a esemplificazione, si non devi sincronizzare nulla.

Non è una cattiva pratica o una buona pratica.Dobbiamo lavorare con un singolo thread, anche in ambienti multithread come i servlet.

Se in futuro si ha a che fare con applicazioni multi thread, si può considerare l'utilizzo di un JavaBean immutabile;)

BTW, l'alternativa per creare fagioli immutabili, e continuano ad offrire un po 'di setter sta usando Builders come :

Employee e = new EmployeeBuilder() 
        .setName("Oscar") 
        .setLastName("Reyes") 
        .setAge(0x1F) 
        .setEmployeeId("123forme") 
        .build(); 

che sembra molto simile alla normale setXyz utilizzato in grani regolari con il vantaggio di utilizzare i dati immutabili.

Se è necessario modificare un valore, è possibile utilizzare un metodo di classe:

Employee e = Employee.withName(e, "Mr. Oscar"); 

che prende l'oggetto esistente, e copiare tutti i valori, e impostare un nuovo ....

public static EmployeeWithName(Employee e , String newName){ 
     return new Employee(newName, e.lastName, e.age, e.employeeId); 
    } 

Ma ancora, in un modello a filo singolo è perfettamente sicuro utilizzare getter/setter.

PS Vi incoraggio vivamente a comprare questo libro: Effective Java. Non te ne pentirai mai e avrai informazioni per giudicare articoli migliori come uno citato.

+0

+1 per buoni contenuti. Ho parlato di mappe nidificate perché uso i bean per altre cose oltre alle righe del database, IE che evolve gli oggetti utente. Tuttavia, dal momento che entrambi si stanno evolvendo, non sono sicuro che la via immutabile, sebbene abbia dei vantaggi, è la migliore qui perché se volessi cambiare l'oggetto, ho ancora problemi di sincronizzazione + cercando di sostituire l'oggetto esistente. – TheLQ

+0

@ Lord.Quackstar Sì, ma la maggior parte delle volte il thread singolo è abbastanza buono e i bean Java non sono un cattivo pattern o altro. Sono solo un'alternativa. – OscarRyz

+0

Una cosa che si potrebbe anche considerare sarebbe quella di avere un metodo toBuilder per ogni bean dato, in modo da poter trasformare qualsiasi bean in un builder, impostare alcuni campi e quindi ricostruire l'oggetto come un nuovo oggetto. Quindi potresti fare employee = employee.toBuilder(). SetName ("Mary Poppins"). Build(); e questo ti darebbe una semi-mutevolezza. –

0

È perfettamente sicuro e molto più preferibile rispetto all'infinitamente annidato java.util.Map. Ottieni sicurezza del tipo, codice più facile da capire ed evita che il tuo codice finisca nel Daily WTF. Si noti che l'implementazione deve essere separata: non mescolare troppa logica (oltre alla semplice convalida) nelle classi di archiviazione dei dati. Si dovrebbe leggere su POJOs

3

"Il modello JavaBeans ha gravi inconvenienti." — Joshua Bloch, Effective Java

amo quei ragazzi. Prendere quotazioni arbitrarie fuori dal contesto è già motivo per non fidarmi di questo articolo.

BTW, libro di riferimento (Java efficace) ha un'ampia discussione su entrambi i modelli, i loro vantaggi, svantaggi e alternative. Potresti voler controllare. Ma non c'è nulla di intrinsecamente sbagliato in JavaBeans, solo a volte non sono la scelta migliore.

Modifica: vedi punto 2 ("considerare un costruttore di fronte a molti parametri del costruttore") in Effective Java su Google Libri: http://books.google.com/books?id=ka2VUBqHiWkC&lpg=PP1&pg=PA11#v=onepage&q&f=false

+0

Sono d'accordo sul fatto che gli argomenti dell'articolo sono molto deboli, basati su citazioni prese fuori dal contesto e l'articolo non propone un'alternativa che sia chiaramente migliore. In altre parole, @ Lord.Quackstar: non credere a nulla che leggi in un articolo a caso su Internet al valore nominale. – Jesper

0

intenzioni del articolo sono buone intenzioni, ma in pratica evitando fagioli e setter è difficile da mantenere. Ad esempio, disporre di framework che utilizzano i costruttori non è possibile in molti casi poiché i nomi sono in genere la funzione di identificazione e i nomi dei parametri di metodo/costruttore non vengono mantenuti nel file .class. (Anche se v'è almeno un library per ovviare a questo.)

Setter sono la cosa migliore - non come OO, ma un enorme vantaggio "puro" su costruttori e lavorare bene in pratica.

I test di Whem hanno esito negativo a causa di "cattivi" java bean, quindi riconsidererò la causa. Ma non l'ho visto finora. L'unica volta che direi che probabilmente sono inappropriati è avere bean condivisi nel codice multithread, poiché la sincronizzazione dello stato mutabile condiviso è complicata. Evitatelo e i fagioli vanno bene.

+0

Cosa considereresti un "cattivo java bean"? – TheLQ

+0

Il "cattivo" era il mio umoristico titolo dell'articolo - non penso che siano intrinsecamente cattivi - ma come tutti gli schemi possono essere abusati per diventare un anti-modello. Forse in alcuni casi i fagioli possono produrre disaccoppiamenti non necessari tra il comportamento e i dati, o dove il getter/setter abituale a linea singola sarebbe meglio fare come un calcolo più ricco. E se solo le proprietà fossero un concetto di prima classe, allora potremmo costruire il modello senza la necessità di stringhe (nomi di proprietà) e riflessione ... – mdma

+0

"Tra l'istanziazione della classe e l'impostazione della proprietà finale si ha un oggetto che è in un interno -inconsistente o inutilizzabile stato, "http://stackoverflow.com/a/3122912/262852 questo è il problema in poche parole. – Thufir

2

Il motivo principale per evitare i setter è l'immutabilità. Codice per immutabilità in primo piano ed evitare problemi di threading intorno a tali oggetti.

Se si finisce con un costruttore che legge

new Person ("param 1", "param 2", "param 3", "param 4", "param 5", "param 6", "param 7", "param 8")

quindi il tuo oggetto è troppo complicato. Avrai bisogno di oggetti Parameter (vedi il libro di rifattorizzazione di Martin Fowler).

Codice in modo difensivo all'inizio e le persone che arrivano e mantengono il tuo codice ti ringrazieranno (bene) o ti malediranno (perché non possono essere pigri e semplicemente mutare oggetti).

Quando è necessario modificare l'oggetto, aggiungere un costruttore di copia (cioè il metodo clone). Le moderne JVM si occupano di questo in modo facile e veloce e c'è poca penalità di velocità. Anche tu fai le cose facili per Hotspot e GC.

5

La mia obiezione all'utilizzo di JavaBeans come classi di archiviazione dati è che consentono oggetti con stato incoerente. Nel tipico caso d'uso per tali bean, avete i seguenti passi:

  • istanziare la classe;
  • imposta la prima proprietà;
  • imposta la seconda proprietà;
  • ...
  • imposta la proprietà finale;
  • utilizzare l'istanza dell'oggetto.

Ora la tua classe è pronta per l'uso. Quindi qual è il problema qui? Tra l'istanziazione della classe e l'impostazione della proprietà finale si ha un oggetto che si trova in uno stato internamente incoerente o inutilizzabile, ma non c'è nulla che impedisce di usarlo accidentalmente.Preferisco un sistema in cui la classe si trovi automaticamente in uno stato coerente e utilizzabile all'istanziazione. Per questo motivo preferisco passare in tutto lo stato iniziale nel costruttore o, se detto stato iniziale è troppo complicato, passare allo stato iniziale sotto forma di mappa hash o set o simili. Lo scenario utilizzo caso è ora:

  • (opzionale: impostare una parametri oggetto);
  • istanziare la classe;
  • utilizzare l'istanza dell'oggetto.

In nessun punto del flusso di lavoro è possibile iniziare accidentalmente a utilizzare un oggetto con stato incoerente. Se utilizzo un oggetto parametri, non viene utilizzato direttamente per nulla e il suo contenuto verrà esaminato in seguito all'istanziazione della mia classe principale. La classe principale stessa, al ritorno dall'istanziazione, mi fornirà un'istanza di oggetto che è di uso immediato.

Questo tipo di configurazione è ideale per oggetti più semplici, ovviamente. Per oggetti più complicati con cose come proprietà opzionali, ecc. Vorrete fare un passo avanti e usare qualcosa come il pattern Builder che altri vi hanno indicato. I builder sono carini quando si hanno scenari più complicati, IMO, ma per una parametrizzazione più semplice e diretta basta usare gli argomenti del costruttore o un oggetto parametri di qualche tipo è più che sufficiente.