2012-06-08 16 views
6

Stavo attraversando il libro di programmazione del Martin Odersky a Scala con la sua sezione sui moduli astratti, e la sua carta Scalable componenti Astrazioni:Moduli astratti alla scala in C# o in altre lingue?

http://lampwww.epfl.ch/~odersky/papers/ScalableComponent.pdf

mio asporto è che, rendendo i moduli classi astratte invece di oggetti (o classici , moduli globali statici come in Java):

abstract class myModule{ 
    // this is effectively an abstract module, whose concrete 
    // components can be configured by subclassing and instantiating it 
    class thing{} 
    class item{} 
    object stuff{} 
    class element{} 
    object utils{} 
} 

puoi creare istanze multiple di sottoclassi e istanze di un modulo con differenti caratteristiche concrete. Ciò consente di configurare il modulo in modo diverso a seconda delle circostanze (ad esempio sostituendo il componente del database durante il test o il componente IO quando si trova in un ambiente di sviluppo) e istanziando più moduli ciascuno con il proprio insieme di stato mutabile nell'ambito del modulo.

Da quello che posso capire, a livello di base, è solo un requisito rigido che è possibile avere classi nidificate, in modo tale che la classe che le include possa agire come un modulo.

È un altro requisito pratico è che è possibile distribuire la definizione della classe su più file, dal momento che un modulo con un gruppo di classi in esso è probabilmente più linee di codice di quanto la maggior parte accetterebbe in un singolo file sorgente.

Scala esegue questa operazione con Tratti, che apportano altre chicche che sono carine ma non centrali per l'intero spread abstract-module-class su più file di origine idea. C# ha partial classes, che fornisce la stessa funzionalità e consente anche classi annidate. Presumibilmente alcune altre lingue hanno un supporto simile per le classi annidate, oltre a dividere una classe su più file.

Questo tipo di schema si verifica ovunque in C# o in altre lingue? Penserei che i progetti di grandi dimensioni in molte lingue affrontino il problema che i moduli astratti sono pensati per risolvere. C'è qualche ragione per cui questa cosa di "classe astratta come modulo astratto" non funzioni, e quindi non viene usata? Mi sembra una soluzione molto più pulita rispetto ai vari framework DI con l'offerta della stessa funzionalità.

+2

Penso che tu sia sfortunato, e il motivo è un fraintendimento: ** le classi parziali non hanno nulla a che vedere con i tratti **. Le classi parziali sono semplicemente un modo di diffondere una definizione di classe su più file, mentre i tratti sono un meccanismo potente (e non esistente in C#) del sistema di tipi./EDIT O forse no, se hai solo bisogno del meccanismo fornito da classi parziali. –

+1

Il mio punto è che sebbene i tratti ti permettano di fare ogni sorta di cose belle con i tuoi moduli astratti, il nocciolo della cosa 'class-as-abstract-module' richiede solo la possibilità di nidificare classi e diffonderle su più file. Potrei sbagliarmi completamente, quindi per favore correggimi se lo sono. –

risposta

2

Il modulo astratto descriveresti ha le seguenti proprietà fondamentali:

  • Si tratta di un modulo chiuso, nel senso che fornisce tutto l'interfaccia che consente l'interazione con il modulo
  • È possibile specificare le operazioni su il modulo
  • È possibile specificare i tipi che fanno parte del modulo - di solito questi tipi sono gestiti dai moduli sopra menzionati
  • Nessuna implementazione è fornita - questo è l'astratto parte : ci possono essere molte implementazioni concrete e che è effettivamente utilizzato nel programma sarà specificato e selezionati dal programma in quanto meglio si adatta alla necessità

La caratteristica di essere in grado di specificare il modulo usando più di un file sorgente non è un requisito fondamentale, ma può certamente tornare utile.

Nella sua forma più semplice, un modulo sta descrivendo un tipo di dati astratto (ad esempio la coda): quali operazioni sono disponibili per interagire con il tipo di dati e tutti i tipi ausiliari necessari per l'interazione.

In una forma più complessa, può descrivere un intero sottosistema (ad esempio il collegamento in rete).

In linguaggi imperativi di solito utilizzare un un'interfaccia per lo stesso scopo:

  • E 'racchiuso
  • È possibile specificare le operazioni
  • È possibile specificare i tipi che fanno parte dell'interfaccia
  • Nessuna implementazione

Come hai detto, se il tuo modulo ha una grande interfaccia (ad es. descrive un sottosistema), di solito non è pratico scrivere le classi implementando l'interfaccia ricca in un unico file. Se la lingua non fornisce il supporto per suddividere la stessa classe in fonti separate (o più precisamente: per "incollare" parti separate della stessa classe da diversi file sorgente insieme), la soluzione di solito è quella di perdere il requisito allegato e fornire una serie di interfacce che specificano le interazioni tra di esse - in tal modo si ottiene un'API per il sottosistema (è un'API nel suo senso più puro: è un'interfaccia per interagire con il sottosistema, senza alcuna implementazione ancora).

In un certo senso quest'ultimo approccio può essere più generico (generico nel senso di ciò che è possibile ottenere con esso) rispetto a un tipo chiuso: è possibile fornire implementazioni dei vari sottotipi (specificati tramite interfacce) di diversi autori : finché i sottotipi si basano solo sull'interfaccia specificata per l'interazione reciproca, l'approccio mix-n-match funzionerà.

Uno dei punti di forza della maggior parte dei linguaggi di programmazione funzionali sono i tipi di dati parametrizzati, in cui si crea un'istanza di un dayatype con un altro come parametro (ad esempio una coda di numeri interi). La stessa flessibilità è ottenuta da Generics in Java/C# (e modelli in C++). Naturalmente le implicazioni esatte e il potere espressivo possono differire tra le lingue in base al loro sistema di tipi.

Questa intera discussione è separata dalla Dipendenza Iniezione (DI), che tenta di allentare la forte dipendenza tra un'implementazione concreta di un tipo e le sue parti di supporto fornendo esplicitamente le parti necessarie (al contrario di avere la scelta scegliere), come l'utente del tipo potrebbe avere una migliore comprensione di quale implementazione di quelle parti sono i migliori per raggiungere il suo obiettivo - ad es fornendo finte implementazioni per testare funzionalità.

Il problema che DI cerca di risolvere è esclusivo dei linguaggi imperativi, è possibile avere gli stessi problemi di dipendenza anche nei linguaggi funzionali: l'implementazione di un modulo astratto potrebbe scegliere di utilizzare un'implementazione specifica di sottotipi (quindi accoppiarsi a quelle implementazioni) invece di prendere le implementazioni sottotipo come parametri (che è ciò che mira a DI)

8

Il solito confronto è con i moduli ML, dove i tratti Scala (o classi astratte) svolgono il ruolo delle firme ML e le loro implementazioni concrete (in genere oggetti Scala) svolgono il ruolo di strutture ML. La discussione sui moduli ML here dovrebbe rendere la connessione ragionevolmente chiara.

L'analogico tra Scala e ML è deliberato e se si guarda il sorgente del compilatore Scala si vedrà che gli oggetti Scala vengono spesso chiamati usando nomi che contengono "Modulo" come parte.

+1

Ad esempio, il nome della variabile nel codice byte Java che fa riferimento a un'istanza Scala 'object' è chiamato' MODULE $'. Molte persone che lavorano con la riflessione potrebbero essersi imbattuti in questo. – Madoc

Problemi correlati