Sto scrivendo un'applicazione per creare fregi musicali e mi trovo in difficoltà su come strutturare le classi.
Ora i componenti sono i seguenti:
codice:
Interface IGuidoFormattable
Class Note : IGuidoFormattable, ICloneable
Class Pause : IGuidoFormattable, ICloneable
Class Chord : IGuidoFormattable, ICloneable
Class Key : IGuidoFormattable, ICloneable
Class Tempo : IGuidoFormattable, ICloneable
Class Meter : IGuidoFormattable, ICloneable
Class FretPiece : List<IGuidoFormattable>, ICloneable
Class Fret : List<FretPiece>
FretPiece costituisce una frase musicale, un pezzo del fregio. Espone come campi Key, Tempo e Meter, che sono omonimi dei rispettivi tipi. Più frasi formano un fregio, rappresentato dalla classe Fret. Ogni elemento in una frase deve essere formattabile secondo la notazione standard GUIDO, perciò deve implementare l'interfaccia IGuidoFormattable.

Non è finita. In un altro namespace sono definite delle "mutazioni", ossia delle classi che attuano vari tipi di modifica sulla frase musicale. Le ho chiamate mutazioni perché all'inizio volevo usare un algoritmo genetico. Esse sono derivate da una delle due classi base astratte:
codice:
Class FretMutation
Class LambdaFretMutation : FretMutation
Infine esiste una classe FretMutationGenerator che rappresenta un oggetto che ha il compito di applicare delle variazioni (memorizzate in un campo di istanza) a una frase passata come parametro e restituire come risultato il fregio completo, un'istanza di Fret.

Ora, ci sono molte cose che non mi piacciono e che rendono il codice un po' "incasinato". Tanto per cominciare, FretPiece deve poter contenere molti elementi diversi (note, pause e accordi in questo caso) che tuttavia devono soddisfare due vincoli: devono essere traducibili in una stringa conforme alla notazione GUIDO; devono essere clonabili. Nella struttura proposta ho fatto implementare a ogni classe l'interfaccia ICloneable e ho supposto che ogni elemento di FretPiece sia clonabile, ma questo non è garantito dalla semantica del codice così come è scritto ora. Devo quindi trovare un modo di esprimere entrambi i vincoli con eleganza.
Escludo di poter applicare l'ereditarietà a IGuidoFormattable per farle ereditare ICloneable, ossia così:
codice:
interface IGuidoFormattable : ICloneable
perché non sussiste alcun legame logico tra le due. Alternativamente potrei aggiungere il metodo Clone alla definizione dei membri dell'interfaccia, ma non so quanto questo sia lecito in termini di qualità del codice.

Secondo e più importante difetto. Per come è scritto ora, ogni classe derivata da FretMutation utilizza il polimorfismo per ridefinire il comportamento del metodo Apply, dichiarato come abstract nella classe base. Questo metodo, come detto precedentemente, ha una signature simile a questa:
codice:
FretPiece Apply(FretPiece originalTheme)
ossia prende in input il tema proposto e ne restituisce una copia modificata, in conformità con tutti i parametri e i valori numerici esposti dalla classe stessa come proprietà. Solo per il fatto che deve per forza restituirne una copia devo imporre che tutti gli elementi di FretPiece siano clonabili, altrimenti incorrerei nelle spiacevoli conseguenze dell'assegnamento di oggetti di tipo reference.
Come se non bastasse, ogni versione di Apply nelle classi derivate si comporta in maniera diversa a seconda della variazione. Alcune agiscono solo sulle note, altre sugli accordi, altre ancora su entrambi. Tuttavia, poiché l'oggetto passato è una lista di IGuidoFormattable, non ho modo di sapere quale sia il tipo dell'elemento se non controllando il valore restituito da GetType: fatto questo il corpo della funzione diventa una sequenza di if, il che fa molto "procedurale" e decisamente poco OO. Cionondimeno, non posso pensare di definire in ogni classe Note, Pause o Chord un metodo che applichi quella variazione, poiché l'entità che essa rappresentano non ha nulla a che vedere con il fatto di poter essere mutata. In sostanza, ogni classe deve essere ignara di tutte le altre e di conseguenza non può arrabattarsi dei metodi che dovrebbero essere propri di ciò che essa non può vedere.

Qualcuno potrebbe obiettare che Note, Pause e Chord sono più che altro strutture dati che non oggetti. A questo risponderei che per prima cosa non posso implementare un accordo se non con una lista (NO array), la quale di per sé è un oggetto; inoltre non potrei pensare di aggiungere strutture dati diverse ad un'unica lista tipizzata.


Qualcuno ha delle idee per aiutarmi?