Solo i miei due centesimi. L'obiettivo di questa risposta è di dare alcuni pensieri e attrarre più risposte, non cercando di sembrare definitivo o addirittura informativo.
Perché gli stream astraggono le classi base invece delle interfacce?
- È possibile definire un'interfaccia strettamente minima.
- In genere questo consiste di: leggere, scrivere, cercare, dire (restituisce la posizione corrente del file), ottenere la lunghezza, troncare (ridurre la dimensione del file), chiudere.
- Tuttavia, per rendere l'implementazione del flusso comoda da usare, sarebbero necessari molti metodi di supporto per la classe.
- Senza questi metodi di supporto, renderebbe impraticabile il linguaggio di programmazione, perché sarebbe quindi troppo fastidioso utilizzare gli stream come caratteristica fondamentale della libreria standard.
- Questi metodi di supporto utilizzano l'interfaccia minima.
- In genere, è possibile fornire implementazioni predefinite (standard) per questi metodi di supporto, che soddisfano completamente le esigenze la maggior parte del tempo.
- Pertanto, le classi base astratte vengono utilizzate per fornire queste implementazioni del metodo helper predefinito per migliorare l'usabilità della libreria standard.
Il metodo close () è fondamentale per l'operazione dei flussi?
Contrariamente al tuo punto di vista, direi che i flussi che non richiedono la chiusura sono la minoranza, piuttosto che la maggioranza.
- Gestisce il file system
- Risorse di proprietà del sistema operativo, come ad esempio:
- Sockets
- Tubi
- Memoria condivisa gestita dal sistema operativo (al contrario della memoria gestita da JVM)
- Connessioni al database
Per dirla senza mezzi termini,
- Sembra che gli stream supportati da array siano il tipo solo di flussi che non richiedono la chiusura esplicita.
L'analisi costi-benefici favorisce l'inclusione del metodo close () nell'interfaccia del flusso?
Sembra così. Per gli stream che non richiedono la chiusura esplicita, è innocuo richiedere che fornisca un banale metodo close()
che non fa nulla.
Un'osservazione simile può essere trovata nel linguaggio C #, dove è comune vedere le interfacce che sottotipo da IDisposable
.
L'errore di includere il metodo close () nell'interfaccia dello stream causerebbe un danno straordinario alle implementazioni che lo richiedono?
Sì. Non averlo sull'interfaccia, o dividerlo in una seconda interfaccia Closeable
causerebbe un danno straordinario.
Nel primo frammento di codice di seguito, supponiamo che InputStream
non contenga il metodo close()
. Ecco come appare il codice:
public void parseAndCloseStream(InputStream strm) throws IOException
{
if (strm == null) { /* ... to avoid NPE inside finally */ }
try
{
// read from stream
}
finally
{
if (strm instanceof Closeable)
{
Closeable c = (Closeable)strm;
c.close();
}
}
}
In altre parole, poiché l'omissione implica che "non tutti gli InputStream sono chiudibili", qualsiasi utente di qualsiasi istanza di InputStream ora ha l'ulteriore responsabilità di verificare se l'istanza sia effettivamente chiudibile.
Il modello try-with
richiederebbe anche più codice, perché non puoi usare un InputStream lì a meno che tu non possa lanciarlo in un Closeable.
È una violazione del principio di segregazione dell'interfaccia?
No.
- Se un metodo è fondamentale per l'operazione per la maggior parte dell'implementazione, ha senso richiederlo.
- Allo stesso modo, se un metodo è coesivo con il resto dei metodi in quell'interfaccia (cioè senso comune per la sua inclusione), non è una violazione.
In particolare, il principio di segmentazione dell'interfaccia non richiede :
- Non richiede un'interfaccia minima; anche se ci sono dei vantaggi nel mantenere un'interfaccia piccola e semplice.
- Non è necessario che uno separi tutti gli aspetti ortogonali di un'interfaccia, se così facendo causerà più danni che benefici (grande disagio).