Manutenzione di un software CAD/CAM per la macchina per il taglio dei metalli. Quindi ho una certa esperienza con questi problemi.
Quando abbiamo convertito per la prima volta il nostro software (è stato rilasciato per la prima volta nel 1985!) Su un oggetto orientato al design, ho fatto esattamente quello che non ti piace. Gli oggetti e le interfacce avevano Draw, WriteToFile, ecc. Scoprire e leggere i Design Pattern a metà della conversione aiutava molto ma c'erano ancora molti odori di codice cattivo.
Alla fine mi sono reso conto che nessuno di questi tipi di operazioni era veramente la preoccupazione dell'oggetto. Ma piuttosto i vari sottosistemi che dovevano fare le varie operazioni. Ho gestito questo utilizzando quello che ora viene chiamato un oggetto comando Passive View e un'interfaccia ben definita tra i livelli del software.
Il nostro software è strutturato fondamentalmente come questo
- Le forme di attuazione varie forme Interface. Queste forme sono una cosa shell che passa eventi al livello dell'interfaccia utente.
- Livello UI che riceve eventi e manipola moduli tramite l'interfaccia Modulo.
- Il livello UI eseguirà i comandi che implementano l'interfaccia Command
- L'oggetto UI ha interfacce proprie che il comando può interagire con.
- I comandi ricevono le informazioni di cui hanno bisogno, lo elaborano, manipolano il modello e quindi segnalano agli oggetti dell'interfaccia utente che quindi eseguono qualsiasi operazione necessaria con i moduli.
- Infine i modelli che contengono i vari oggetti del nostro sistema. Come programmi forma, percorsi di taglio, tavolo da taglio e fogli di metallo.
Così Disegno viene gestito nel livello dell'interfaccia utente. Abbiamo diversi software per diverse macchine. Quindi, mentre tutti i nostri software condividono lo stesso modello e riutilizzano molti degli stessi comandi. Gestiscono cose come disegnare in modo molto diverso. Ad esempio un tavolo da taglio è diverso per una macchina router rispetto a una macchina che utilizza una torcia al plasma nonostante entrambi siano essenzialmente un gigantesco tavolo piatto X-Y. Questo perché, come le macchine, le due macchine sono costruite in modo diverso in modo tale da creare una differenza visiva per il cliente.
Per quanto riguarda le forme quello che facciamo è la seguente
Abbiamo programmi di forma che producono percorsi di taglio attraverso i parametri immessi. Il percorso di taglio conosce quale programma di forme è stato prodotto. Tuttavia un percorso di taglio non è una forma. Sono solo le informazioni necessarie per disegnare sullo schermo e tagliare la forma. Una ragione per questa progettazione è che i percorsi di taglio possono essere creati senza un programma di forma quando vengono importati da un'app esterna.
Questo design ci consente di separare il design del percorso di taglio dal design della forma che non è sempre la stessa cosa. Nel tuo caso probabilmente tutto ciò che ti serve per pacchettizzare sono le informazioni necessarie per disegnare la forma.
Ogni programma forma ha un numero di viste che implementano un'interfaccia IShapeView. Attraverso l'interfaccia IShapeView il programma di forma può dire al modulo di forma generico che abbiamo come impostarlo per mostrare i parametri di quella forma. La forma di forma generica implementa un'interfaccia IShapeForm e si registra con l'oggetto ShapeScreen. L'oggetto ShapeScreen si registra con il nostro oggetto applicazione. Le viste di forma usano qualsiasi schermo di forma che si registri con l'applicazione.
Il motivo delle molteplici visualizzazioni che abbiamo clienti a cui piace inserire forme in modi diversi. La nostra base di clienti è divisa a metà tra coloro che desiderano inserire i parametri di forma in una tabella e quelli che desiderano entrare con una rappresentazione grafica della forma di fronte a loro. Abbiamo anche bisogno di accedere ai parametri a volte attraverso una finestra di dialogo minima piuttosto che la nostra schermata di immissione della forma completa. Da qui le molteplici visualizzazioni.
I comandi che manipolano le forme rientrano in una delle due categorie. O manipolano il percorso di taglio o manipolano i parametri di forma. Per manipolare i parametri della forma in genere, li gettiamo nella schermata di immissione della forma o mostriamo la finestra di dialogo minima. Ricalcolare la forma e visualizzarla nella stessa posizione.
Per il percorso di taglio abbiamo raggruppato ciascuna operazione in un oggetto comando separato. Per esempio abbiamo oggetti comando
ResizePath RotatePath MovePath SplitPath e così via.
Quando abbiamo bisogno di aggiungere nuove funzionalità, aggiungiamo un altro oggetto comando, troviamo un menu, una scorciatoia da tastiera o una barra degli strumenti nella schermata dell'interfaccia utente destra e configuriamo l'oggetto UI per eseguire il comando.
Ad esempio
CuttingTableScreen.KeyRoute.Add vbShift+vbKeyF1, New MirrorPath
o
CuttingTableScreen.Toolbar("Edit Path").AddButton Application.Icons("MirrorPath"),"Mirror Path", New MirrorPath
In entrambi i casi i oggetto MirrorPath Command viene associato ad un elemento dell'interfaccia utente desideri. Nel metodo execute di MirrorPath è tutto il codice necessario per riflettere il percorso in un particolare asse. Probabilmente il comando avrà una propria finestra di dialogo o utilizzerà uno degli elementi dell'interfaccia utente per chiedere all'utente quale asse riflettere. Nessuno di questi sta facendo un visitatore, o aggiungendo un metodo al percorso.
Troverete che molto può essere gestito attraverso le operazioni di raggruppamento in comandi. Tuttavia, avverto che non si tratta di una situazione in bianco o nero. Troverete ancora che certe cose funzionano meglio come metodi sull'oggetto originale. Nell'esperienza maggio ho scoperto che forse l'80% di quello che facevo nei metodi era in grado di essere spostato nel comando. L'ultimo 20% funziona semplicemente meglio sull'oggetto.
Ora a qualcuno potrebbe non piacere perché sembra violare gli incapsulamenti. Dal mantenimento del nostro software come sistema orientato agli oggetti nell'ultimo decennio, devo dire che la cosa più importante a lungo termine che puoi fare è documentare chiaramente le interazioni tra i diversi livelli del tuo software e tra i diversi oggetti.
Il raggruppamento di azioni in oggetti di comando aiuta a questo obiettivo molto meglio di una devozione servile agli ideali di incapsulamento.Tutto ciò che è necessario fare per riflettere un percorso è raggruppato nell'oggetto comando Percorso specchio.
Solo una nota a parte, in quanto è improbabile che sia possibile modificare la lingua: Esistono lingue che supportano direttamente più funzioni generiche di invio. – Svante
Ottima domanda. Volevo solo fornire un contrappunto. A volte il tuo problema con (5) può essere una buona cosa. Uso il pattern visitor quando ho alcune funzionalità che devono essere aggiornate quando viene definito un nuovo sottotipo IShape. Ho un'interfaccia IShapeVisitor che definisce quali metodi sono necessari. Finché l'interfaccia viene aggiornata con il nuovo sottotipo, il mio codice non viene creato fino a quando non viene aggiornata la funzionalità critica. Per alcune situazioni, questo può essere molto utile. – oillio
Sono d'accordo con @oillio, ma potreste applicarlo come metodo astratto su IShape. Ciò che il modello Visitor ti compra in un linguaggio OO puro è la località della funzione (rispetto alla località della classe) e quindi una separazione delle preoccupazioni.In ogni caso, se si desidera forzare l'aggiunta di nuovi tipi da riesaminare attentamente, lo schema del visitatore deve essere esplicitamente interrotto in fase di compilazione! –