Consentitemi di prefarti dicendo che conoscere alcune teorie musicali elementari e la notazione musicale può essere utile per cogliere il problema in questione.
Attualmente sto costruendo un Notazione musicale e un Editor di tablature (in Javascript). Ma sono giunto al punto in cui le parti principali del programma sono più o meno lì. Tutte le funzionalità che ho in programma di aggiungere a questo punto potranno davvero costruire le fondamenta che ho creato. Di conseguenza, voglio refactoring per davvero consolidare il mio codice.
Sto utilizzando un'API chiamata VexFlow per eseguire il rendering della notazione. Fondamentalmente passo le parti dello stato dell'editor a VexFlow per costruire la rappresentazione grafica dello spartito.
(EDIT: il seguente diagramma è obsoleto quando ho iniziato a rielaborare la struttura, per favore vedi l'aggiornamento in fondo al post per il nuovo diagramma) Ecco un diagramma UML approssimativo e semplificato che mostra il profilo del mio programma:
In sostanza, un Part
ha molti Measure
s che ha molti Note
s che ha molti NoteItem
s (sì, questo è semanticamente strano, poiché un accordo è rappresentato come Note
con multipli NoteItem
s, singoli passi o posizioni dei tasti). Tutte le relazioni sono bidirezionali.
Ci sono alcuni problemi con il mio design perché la mia classe Measure
contiene la maggior parte della logica dell'intera vista dell'applicazione.
-
La classe contiene i dati su tutti gli oggetti VexFlow (la rappresentazione grafica della partitura). Contiene l'oggetto grafico Staff e le note grafiche. (Non dovrebbero essere collocati da qualche altra parte nel programma?)
-
Mentre
VexFlowFactory
si occupa della creazione effettiva (e dell'elaborazione) della maggior parte degli oggetti VexFlow,Measure
continua a "indirizzare" la creazione di tutti gli oggetti e l'ordine in cui devono essere creati per sia VexFlowStaff che VexFlowNotes.
Non sto cercando una risposta specifica perché avresti bisogno di una comprensione molto più profonda del mio codice. Solo una direzione generale per entrare.
Ecco un pensiero che ho avuto, creare una MeasureView
/ NoteView
/ PartView
classi che contiene gli oggetti VexFlow di base per ogni classe oltre a qualsiasi logica estranea per la sua creazione? ma dove sarebbero contenute queste opinioni? Creo un ScoreView
che è una rappresentazione grafica parallela di tutto? In modo che ScoreView.render()
precipiterebbe verso il basso PartView
e chiamerà render
per ogni PartView
e diminuirà in ogni MeasureView
, ecc. Ancora una volta, non ho idea di quale direzione andare. Più penso a più modi di apparire sembrano saltarmi in testa.
Ho cercato di essere il più conciso e semplicistico possibile mentre continuavo a risolvere il mio problema. Non esitate a farmi domande se qualcosa non è chiaro. È una vera sfida cercare di mettere a tacere un problema complicato alle sue parti principali.
UPDATE: ------------------------------------------ --------------------------------
Quindi sono andato avanti e ho iniziato a rifattare la mia idea dall'alto. Ecco un diagramma aggiornato
@ JW01 Capisco perché le relazioni bidirezionali siano potenzialmente cattive (difficile da mantenere, aumento della complessità), tuttavia, ritengo che la maggiore flessibilità sia utile e l'ho implementata in modo sostenibile. Ogni "contenitore" di notazione ( Score
, Part
, Measure
, Note
- tutti contengono figli di qualche tipo) eredita da Container
(questa relazione non è mostrata nello schema perché il programma che uso è merdoso e sembra molto disordinato). Il metodo Container.addItem(item)
si mescola nei metodi di classe Traversable
e nella proprietà parent
nell'elemento aggiunto.
Per me questo è sembrato abbastanza intelligente e facile da capire. In sostanza, qualsiasi elemento che viene inserito in un contenitore ottiene funzionalità estese. Come menzionato in un precedente commento, potrei voler estrarre una bella API di dati musicali da questo, che consentirebbe di creare facilmente "plug-in". Attraversare in entrambe le direzioni sarebbe particolarmente utile per una funzionalità del genere. Inoltre, queste classi sono piccole e manutenibili e difficilmente modificabili.
Tuttavia, capisco che meno cose dipendono da queste relazioni, meglio è. Quindi è sicuramente meglio isolare questa funzionalità di attraversamento da utilizzare con il modulo Selection
.
Sto notando che le mie classi View
stanno facendo molto ora. Costruzione, rendering, formattazione e gestione dei clic. Probabilmente sarebbe saggio calcolare un modulo View astratto composto da ViewBuilder, ViewFormatter, ViewRenderer, ClickBehavior?