Ho un albero di sintassi astratto che voglio compilare in diverse rappresentazioni. Ora sto lottando per organizzare le lezioni in modo che le nuove rappresentazioni possano essere aggiunte facilmente.
Il modo più semplice per ottenere ciò è aggiungere un metodo per ogni rappresentazione, ad es. compile_to_foo
, compile_to_bar
. Rappresentazioni aggiuntive possono essere aggiunte mediante patch di scimmia. Il problema è che le implementazioni delle compilation sono diffuse ovunque e che violano il principio della singola responsabilità. Il vantaggio è che la compilazione può essere ereditata.
Ora, potrei anche definire una funzione di compilazione contenente uno switch gigante che viene inviato al tipo di argomento. Ma questo perde i vantaggi del polimorfismo e rende più difficile il comportamento ereditario della compilazione. Questa non è un'opzione valida.
Una soluzione interessante userebbe una fabbrica astratta:
Questodesignsembraabbastanzapromettente,mapresentaalcunisvantaggi:
- LagerarchiadelnodoASTnonpuòessereestesasenzaestendereanche
AbstractCompiler
e,asuavolta,tuttiicompilatoriconcretielelorogerarchieparalleledinodiconcreti. - LeinformazionidisottotipizzazionedeinodiASTsonospeadsututtoilsistema.deveesserespecificatotra
AbstractNode
spercondividereilcomportamento(useròiruoli),traConcreteNode
spercondividerecompile
implementazioniealmenonelAbstractCompiler
perfornireimplementazionipredefinite(es.methodNodeA(){returnNode()}
).Questopotrebbeessereparzialmenterisoltotramitemetaprogrammazione. - QuandovienecreatounAST,questopuòcompilaresolofinoallarappresentazioneone.Sevoglioaverepiùuscite,hobisognodiricostruirel'ASTconundiverso
ConcreteCompiler
.
Idealmente,passereisemplicementeun'istanzadelcompilatoreconcretocomeparametroalmetodocompile
:
... ma non ho idea di come il metodo compile
possa ottenere l'effettiva implementazione da una gerarchia di classi parallele (senza usare di nuovo uno switch gigante sul tipo di nodo).
Ho anche studiato il pattern Bridge, ma la soluzione non sembra applicabile al mio problema senza creare mille piccoli ponti.
Ho letto attentamente questa domanda precedente: "Progettare un Architettura robusta per più tipi di esportazione? ". La differenza principale è che i dati di input (lì: equivalenti, rappresentazioni di dati standalone) sono ora nodi AST gerarchici, quindi l'ereditarietà tra le implementazioni di compilazione è cruciale.
Il sistema sarà implementato in Perl, quindi non sono limitato al classico OOP, ma posso anche usare Metaprogramming, Roles (aka. tratti) e Functional Programming.
Cosa mi manca? Esiste un'architettura che potrei usare per strutturare elegantemente questo sistema? Come posso rendere la classe corrispondente dalla gerarchia della classe parallela rilevabile alle classi del nodo, senza sacrificare il polimorfismo?