La coerenza dovrebbe essere favorita rispetto alla convenzione di programmazione?

14

Quando si progetta una classe, dovrebbe essere favorita la coerenza del comportamento rispetto alla pratica di programmazione comune? Per dare un esempio specifico:

Una convenzione comune è questa: se una classe possiede un oggetto (ad es. lo ha creato) è responsabile di ripulirlo una volta fatto. Un esempio specifico potrebbe essere in .NET che se la tua classe possiede un oggetto IDisposable dovrebbe eliminarlo alla fine della sua vita. E se non lo possiedi, non toccarlo.

Ora, se osserviamo la classe StreamWriter in .NET, possiamo trovare nella documentazione che chiude il flusso sottostante quando viene chiuso / eliminato. Ciò è necessario nei casi in cui il StreamWriter viene istanziato passando un nome file mentre il writer crea il flusso di file sottostante e pertanto deve chiuderlo. Tuttavia si può anche passare in un flusso esterno che lo scrittore chiude anche.

Questo mi ha infastidito un sacco di volte (sì, so che puoi creare un involucro non chiudibile ma non è questo il punto) ma a quanto pare Microsoft ha preso la decisione che è più coerente chiudere sempre il flusso, non importa da dove provenga .

Quando mi imbatto in un modello di questo tipo in una delle mie classi di solito creo un flag ownsFooBar che viene impostato su false nei casi in cui FooBar viene iniettato tramite il costruttore e vero altrimenti. In questo modo la responsabilità di pulirlo viene passata al chiamante quando passa esplicitamente l'istanza.

Ora mi chiedo se forse la coerenza dovrebbe essere a favore delle migliori pratiche (o forse la mia migliore pratica non è così buona)? Qualsiasi argomento per / contro di esso?

Modifica per chiarimenti

Con "coerenza" intendo: il comportamento coerente della classe che acquisisce sempre la proprietà (e chiude il flusso) rispetto alla "best practice" per assumere solo la proprietà di un oggetto se lo hai creato o se ne è stata esplicitamente trasferita la proprietà.

Come per un esempio in cui è entusiasmante:

Supponi di avere due classi date (da una libreria di terze parti) che accettano uno stream per fare qualcosa con esso, come la creazione e l'elaborazione di alcuni dati:

 public class DataProcessor
 {
     public Result ProcessData(Stream input)
     {
          using (var reader = new StreamReader(input))
          {
              ...
          }
     }
 }

 public class DataSource
 {
     public void GetData(Stream output)
     {
          using (var writer = new StreamWriter(output))
          {
               ....
          }
     }
 }

Ora voglio usarlo in questo modo:

 Result ProcessSomething(DataSource source)
 {
      var processor = new DataProcessor();
      ...
      var ms = new MemoryStream();
      source.GetData(ms);
      return processor.ProcessData(ms);
 }

Questo fallirà con un'eccezione Cannot access a closed stream nell'elaboratore di dati. È un po 'costruito ma dovrebbe illustrare il punto. Ci sono vari modi per risolverlo, ma tuttavia ritengo di aggirare qualcosa che non dovrei avere.

    
posta ChrisWue 12.01.2012 - 00:17
fonte

4 risposte

9

Anche io uso questa tecnica, la chiamo 'handoff', e credo che meriti lo stato di un pattern. Quando un oggetto A accetta un oggetto monouso B come parametro del tempo di costruzione, accetta anche un booleano chiamato "handoff", (che assume come valore predefinito false) e se è vero, quindi lo smaltimento di A cascade per lo smaltimento di B.

Non sono favorevole alla proliferazione delle scelte sfortunate degli altri, quindi non accetterei mai le cattive pratiche di Microsoft come una convenzione consolidata, né considererei il seguito cieco degli altri delle spiacevoli scelte di Microsoft di essere "comportamenti coerenti" in alcun modo, forma o forma.

    
risposta data 12.01.2012 - 01:02
fonte
3

Penso che tu abbia ragione, ad essere onesti. Penso che Microsoft abbia incasinato la classe StreamWriter, in particolare, per i motivi che descrivi.

Tuttavia, ho visto un sacco di codice in cui le persone non cercano nemmeno di disporre del proprio stream perché il framework lo farà per loro, quindi risolverlo ora farà più male che bene.

    
risposta data 12.01.2012 - 00:26
fonte
2

Vorrei andare con coerenza. Come hai detto alla maggior parte delle persone, ha senso che se il tuo oggetto crea un oggetto figlio, lo possederà e lo distruggerà. Se viene dato un riferimento dall'esterno, ad un certo punto nel tempo "l'esterno" tiene anche lo stesso oggetto e non dovrebbe essere posseduto. Se si desidera trasferire la proprietà dall'esterno nell'oggetto, utilizzare i metodi Attach / Detach o un costruttore che accetta un valore booleano che chiarisca che la proprietà è stata trasferita.

Dopo aver digitato tutto ciò per una risposta completa, penso che la maggior parte dei modelli di progettazione generici non rientrino necessariamente nella stessa categoria delle classi StreamWriter / StreamReader. Ho sempre pensato che la ragione per cui MS ha scelto di far sì che StreamWriter / Reader assuma automaticamente la proprietà è perché in questa specifica istanza, avere una proprietà condivisa non ha molto senso. Non è possibile avere contemporaneamente due oggetti diversi in lettura / scrittura nello stesso flusso. Sono sicuro che potresti scrivere qualche tipo di condivisione del tempo deterministica, ma quello sarebbe un inferno di anti-modello. Quindi questo è probabilmente il motivo per cui hanno detto, dato che non ha senso condividere un flusso, prendiamolo sempre. Ma non considererei questo comportamento come una convenzione generica generalizzata per tutte le classi.

Potresti considerare gli Stream come un pattern coerente in cui l'oggetto che viene passato non è condivisibile dal punto di vista del design e quindi senza alcun flag aggiuntivo, il lettore / scrittore ne riprende la proprietà.

    
risposta data 12.01.2012 - 03:13
fonte
2

Stai attento. Quello che pensi come "coerenza" potrebbe essere solo una raccolta casuale di abitudini.

"Coordinare le interfacce utente per coerenza", SIGCHI Bulletin 20, (1989), 63-65. Spiacente, nessun collegamento, è un vecchio articolo. "... un workshop di due giorni di 15 esperti non è stato in grado di produrre una definizione di coerenza". Sì, si tratta di "interfaccia", ma credo che se si pensa alla "coerenza" per un po ', scoprirai che non puoi definirlo neanche.

    
risposta data 12.01.2012 - 03:30
fonte

Leggi altre domande sui tag