Voglio gestire le note musicali in un programma C # e ho scritto un paio di lezioni per questo. Tuttavia, penso di avere problemi nel rispettare il principio di ASCIUTTO. O questo o io sto pensando troppo alle cose. Scusa se la domanda è già stata trattata da qualche altra parte, non ho potuto pensare a parole chiave più esplicite e ancora non ho trovato nulla sul mio problema.
Non sono riuscito a trovare un esempio più semplice che si adattasse alla spiegazione del mio problema e non sapevo cosa includere e cosa lasciare fuori dalla mia domanda, quindi ho scritto una versione rapida e una versione dettagliata della domanda.
Sfondo
Diciamo che esistono sette etichette per descrivere tutte le note musicali (lettere dalla A alla G). Naturalmente, ci sono più di sette note per ogni composizione, quindi solo sette etichette non erano sufficienti. Quindi abbiamo iniziato a metterli su scaffali ( ottave ). Una nota potrebbe condividere la stessa etichetta di un'altra, ma potrebbe essere su uno scaffale diverso (uno più alto o uno più basso). Questo è il motivo per cui troviamo note C che sono più alte di altre note C.
A causa di tutto ciò, le note di etichettatura non sono banali nella rappresentazione del computer: ci sono due informazioni separate (etichetta e scaffale), le etichette si avvolgono quando una nota si sposta da uno scaffale all'altro e deve essere gestita.
Ho la seguente classe che incapsula entrambe le informazioni sull'etichetta e sull'ottava:
public class OctaveNote
{
// Note is an enum storing my seven labels.
private Note n;
private byte o;
public OctaveNote(Note note, byte octave)
{
n = note;
o = octave;
}
}
Ho anche queste due funzioni membro utilizzate per inserire una nota in alto o in basso:
public OctaveNote PitchUp(byte tones)
{
byte octaveShift = (byte)(tones / 7);
byte newOct = (byte)(o + octaveShift);
char noteShift = (char)(tones % 7);
Note newNote = (Note)((((int)(n + noteShift) % 7) + 7) % 7);
if (newNote < n)
{
newOct++;
}
return new OctaveNote(newNote, newOct);
}
public OctaveNote PitchDown(byte tones)
{
byte octaveShift = (byte)(tones / 7);
byte newOct = (byte)(o - octaveShift);
byte noteShift = (byte)(tones % 7);
Note newNote = (Note)((((int)(n - noteShift) % 7) + 7) % 7);
if (newNote > n)
{
newOct--;
}
return new OctaveNote(newNote, newOct);
}
Il problema
Il codice è un po 'complicato, quindi diciamo che la logica non è rilevante per ora. Il vero problema è qui: i due metodi sono identici , tranne per le tre dichiarazioni:
n + noteShift vs. n - noteShift
newNote < n vs. newNote > n
newOct++ vs. newOct--
Vorrei unire i due metodi per evitare la duplicazione del codice. tones
non sarebbe un byte
ma un char
, e potrebbe assumere valori negativi per esprimere l'inclinazione verso il basso, in questo modo:
n.Pitch(3) => n.PitchUp(3)
n.Pitch(-4) => n.PitchDown(4)
La risposta ingenua sarebbe quella di inserire alcune dichiarazioni if
qua e là per verificare se tones
è negativo ed eseguire le operazioni di conseguenza, ma non c'è un modo più elegante? Cioè, è possibile scrivere un singolo codice frammento che si comporta in modo diverso quando tones < 0
?
Il numero 7 viene usato molto perché (in questo caso) un'ottava è composta da 7 toni. È usato per trovare lo shift di ottava (divisione intero) e lo spostamento di nota (mod).
L'unica parte difficile è l'assegnazione di newNote
, perché n - noteShift
può essere negativo e la mod può produrre un valore non valido per tornare indietro nel mio enum, quindi l'ho aggirato aggiungendo 7 prima di modificare di nuovo .
Per i non musicisti là fuori:
- Le note sono rappresentate da lettere dalla A alla G e si avvolgono su entrambe le estremità:
A
B
C
D
E
F
G
A
B
C
. .. - Quindi, ci possono essere diverse note con la stessa etichetta, ad esempio una nota C più alta di un'altra nota C.
- Per distinguerli, abbiamo ottave . Ad esempio,
C5
è un'ottava sopraC4
. - Gli ottavi si spostano dopo aver raggiunto la nota C. Questo è (parzialmente) perché la scala più nota inizia su C, ed è per questo che il mio
enum
inizia anche su C. La seguente sequenza è nota nel loro ordine naturale: ...G3
A3
B3
C4
D4
E4
F4
G4
A4
B4
C5
D5
. ..