Sunday, June 10, 2007 

Metaprogramming, OOP, AOP (Part 3)

We can now take a look at localization from the AOP perspective. While doing so, I'll use the Virtual Machine Metaphor I introduced in a previous post. Just like before, my goal is not to solve the problem per se, but to explore the designer's reasoning when a specific perspective (or metaphor) is adopted.

AOP, like OOP, is not a universal solution to programming problems. There is a set of problems that can be tackled more effectively using AOP, just like there is a set of problems that can be tackled more effectively using OOP.
When we design the OOP way, for instance, we often try to capture polymorphic behaviour (functional and non-functional behaviour), so to maximize the extensibility of the resulting design.
We often hear that AOP is mostly about non-functional, cross-cutting behaviour, like tracing, exception handling, transactions. The problem with the functional/non functional distinction, however, is that sometimes the distinction is a little blurred (I mentioned QoS in a previous post).
Now, is localization non-functional? I would say so, but this could be open for debate. Is localization cross-cutting? As we saw in the previous post, it usually is, although with some effort we can encapsulate it neatly in a LocalizedString class (assuming we're designing the base framework; we can't retrofit this solution without changing the source code). Again, it seems a little blurry.

Let's try the Virtual Machine Metaphor and see if it gets any clearer. It's really quite simple: all you have to do is ask yourself, is it theoretically conceivable to build a virtual machine that, when running my program, will take care localization? If your guts answer is yes, then you can go further with the metaphor, without getting down to the gory details of pointcuts and advices (or other implementation techniques) too soon.

A virtual machine, to be any useful, must intercept some events and add (or change) behaviour. For instance, your operating system is acting as a virtual machine when it provides you with page-based virtual memory.
Therefore, to make localization a virtual machine concern, the virtual machine needs a way to intercept either your (read) access to localizable strings, or the graphical rendering of localizable strings. In both cases, it has to return (or render) the localized string instead.

Here we see need for the first AOP tenet: quantification; see the paper from Filman and Friedman that I mentioned in Some notes on AOP. We must teach the virtual machine where interception is needed; in other words, the localization behaviour of our virtual machine must be quantified over the type system. This can be done (basically) in three ways:

1) Through a direct mapping with a type: this is akin to have a LocalizableString class. The virtual machine would then intercept any attempt to read a LocalizableString object, recover enough context, access a dictionary, and return the localized string (we still share the same problem with "localization context" that we had with OOP; I'll get back to this later.). It may seem more difficult to apply this strategy at the rendering level, as there is an unbound number of classes that will render text to the GUI, through an unbound set of arbitrarily named methods. As we did in the OOP case, however, we could try to look at a finer granularity: if there is a bound set of classes/methods in the supporting library where text can be rendered, the virtual machine could intercept those calls instead. Note, however, that at this point the virtual machine would have no way to know whether the text must be localized or not. Trace-based AOP systems may help, but when you have an hard time picturing a viable interception strategy for your virtual machine, it's probably better to look at one of the following alternatives.

2) Through decorations inside the application code. Examples of decorations are .NET attributes, Java annotations, but also the old-fashioned marker interfaces or plain ugly naming standards :-). The virtual machine would then intercept any attempt to read a regular string object, provided it has been tagged with some decoration. Note the subtle difference with above. You don't need to specify a type, but you do need to somehow tell your virtual machine where interception is needed. You keep your type system "clean" from the localization concern at the class level, but not at the data member level. Again, this strategy is fine for string access, but not so much for rendering.

3) Through explicit interception instructions, outside the application (functional) code. These instructions tells the virtual machine where a change in behaviour is required. They can be encoded in a separate XML file, or written in an AOP language like AspectJ. These instructions, in turn, can use some universal quantification (mostly through wildcards and regular expressions) or explicit naming of nonconforming instances. For instance, we may want to intercept read access to all string properties in all classes derived from a specific form type, plus read access in a finite set of classes, except a few properties we don't want to localize. The more powerful the quantification language you have, the better and more concisely you can define the set of points where interception is needed. Note that once again, intercepting rendering seems easy, but it's nowhere trivial to understand when localization is needed (more on this below). This may suggest, again without much low-level thinking, that intercepting string access is more viable than intercepting text rendering.

Each way has its own merits, and we can now look at them under the second AOP tenet: obliviousness.

Strategy (1), direct mapping to a type, allows for callee obliviousness (the callee being, in this case, the virtual machine). Indeed (still ignoring context), the virtual machine would have no need to know anything about the caller: it will just provide a uniform service every time a LocalizableString is accessed. There won't be, however, caller obliviousness, as the caller will have to use the appropriate type to enable localization.

Strategy (2) is very similar to (1), as far as obliviousness is concerned. The caller has to add decoration (no obliviousness). The callee (again, the virtual machine) will enjoy complete obliviousness: every tagged string must be localized with uniform behaviour.

Strategy (3) leads to caller obliviousness, and as such, is particularly interesting for legacy code (more on this on a future post about AOP, but if you compare the OOP and AOP approach about localization, this should strike you as the biggest difference!). The callee can still be oblivious. The explicit instructions, however, won't be. They are highly dependent on the caller, and if what you got is a legacy application, you're bound to have a non-uniform set of interception points. We may decide to go with low-level text rendering interception, trying to get caller, callee, and interception instructions obliviousness. This is not going to work. You'll need trace-based interception to discriminate when translation is needed, and the trace will be highly caller-dependent.

Just a few words on context: if you consider (for instance) the form name as the localization context, every strategy will need to recover the form name as well. I'll leave this as the dreaded exercise for the reader :-).

So here it is: by thinking about how a virtual machine could provide a service, we can quickly evaluate the fitness of AOP as a candidate solution for a problem; you can also reason about different interception flavors (depending on the implementation language, you may have more than one option) and evaluate pros and cons. The same metaphor can be applied in mixed situations (e.g. attributes or annotations + reflection), where we accept a few explicit calls in exchange for largely oblivious caller/callee in a traditional OOP language.

As we noted, the biggest difference with good ol' OOP lies in strategy (3), where no knowledge is stored at the type level (or data member level). There, we can appreciate a significant contribution of AOP to the evolution of existing systems, that's often forgotten in language design, but which is probably more interesting than factoring out logging and transactions :-). Stay tuned!

Labels: ,

Ok, adesso mi è chiaro il concetto di virtual machine come metafora e dei cross-cutting concerns. Ma a questo punto la domanda è un'altra: in passato lamentavi che la totalità degli esempi in letterattura riguardava non-functional concern, mentre mi è sembrato di capire che tu pensavi potesse essere utile anche per i functional concern (un esempio sarebbe d'uopo :)). Ma se il concern è functional, non sarebbe sbagliato tirarlo fuori dalla principal decomposition? Non potrebbe essere in effetti indice di un errata decomposizione?
Ottima osservazione, Fulvio :-), a cui provo per ora a rispondere in breve, ed in futuro in modo piu' elaborato.
Intanto devo dire che la distinzione "classica" tra functional / non functional concern andrebbe un po' rivista. In fondo, se noi "estraiamo" il concern transazionale, in genere lo facciamo per eliminare un dettaglio implementativo (cross-cutting) dal codice, ma e' difficile dire che la transazionalita' e' un aspetto non-funzionale. E' invece un aspetto infrastrutturale, e seguendo un po' le orme di quanto dicevo tempo fa, credo sia interessante guardare all'AOP come una eccellente tecnica per modellare aspetti infrastrutturali e pervasivi. Anche perche' in alcune situazioni si fa fatica a distinguere: cambiare la skin grafica e' funzionale o non funzionale? ecc.
Arriviamo pero' al sodo: rimanendo invece nel modello functional / non-functional, e' "giusto" estrarre un concern dalla principal decomposition? E' qui che bisogna pensare da progettisti :-). Il "giusto" non esiste fuori da un contesto. La cosa "giusta" e' quella che bilancia al meglio le forze in gioco. Riprendendo ed elaborando un esempio che ha portato tempo fa Michelangelo Riccobene (, io posso trovarmi a modellare una tassonomia degli skill per varie ragioni, ed una tassonomia dei ruoli aziendali per altre ragioni, ed entrambi i concetti possono essere visti come "aspetti" (funzionali) del concetto di persona (o impiegato). Certamente, posso modellarli entrambi all'interno di un'unica decomposizione principale (ci si riesce senza problemi). Ma posso anche decidere di puntare sulla separation of concern, perche' bilancia meglio alcune forze al contorno (codice legacy, team distribuiti, linee di evoluzione futura divergenti, ecc ecc). E' qui che come progettisti abbiamo un grande beneficio dall'avere a disposizione un materiale piu' malleabile, come quello costituito da un linguaggio target con OOP e AOP...
Visto che è collegato commento qui: Stavo riflettendo sul policy based design di Alexandrescu. In effetti la conclusione a cui sono arrivato è più o meno questa: le policy di Alexandrescu sono aspetti dell'aop, solo che a differenza dei linguaggi aspect oriented il client ha l'onere e l'onore di definire i crosscutting point (cade la metafora della virtual machine :). Certo i crossingcutting point vanno ridefiniti in ogni client, ma c'è anche l'aspetto positivo di poterli definire con granularità più fine e possiamo comunque separare i concern!
Ci ho azzeccato??? :D
In effetti il policy based design e' un tentativo di portare all'interno del C++ nuove tecniche di separation of concern, argomento centrale dell'AOP.
In contesti molto ben delimitati, le policy permettono effettivamente di "estrarre" un concern da una classe, con tutta una serie di pro e di contro.
Purtroppo la loro applicazione e' decisamente limitata, anche qui per tutta una serie di ragioni, e come osservavi sparisce quella caller obliviousness che e' uno dei vantaggi piu' forti dell'AOP.
Pero' si, il ragionamento di fondo e' corretto...
Carlo, la metafora della virtual machine mi pare utilissima per stabilire l’allocazione delle responsabilità. Tuttavia secondo me espone ad un pericolo: pensando ad alcuni concern come funzionalita' di una macchina virtuale si può a ragione pensare che essi debbano restare abbastanza generici come lo sono le macchine virtuali comuni. E di conseguenza si potrebbe essere portati a scegliere una obliviousness del chiamato (la VM) e quindi una quantificazione che fa uso di tipi appositi (hook) nel dominio del problema o di decoratori (strategia 1 o 2 nel tuo post).

Mi viene in mente il problema che si presenta fra classi di dominio e classi di "servizio". Come il principio di dipendenza inversa (DIP) spiega, entrambe devono e possono rimanere indipendenti. Come noto lo si fa creando una classe concreta che deriva dalla classe di dominio e da quella di servizio. E’ un mettere all’esterno, in un componente che e’ relativo solo alla particolare applicazione che stiamo sviluppando, le dipendenze fra componenti diversi e non correlati.

Lo stesso problema si presenta nel caso da te esaminato della localizzazione delle stringhe. Solo che adesso il problema e' "sparpagliato" nel codice. C’e’ una dipendenza fra, ad esempio, classe Form e classe LocalizedString (o LocalizableString). La prima ha bisogno, nell’applicazione corrente, della seconda. Si potrebbe aggredire il problema anche pensando ad una classe Form con il tipo String come policy, come dice Fulvio, ma dovremmo avere anche altre classi con policy (non e’ solo la Form a richiedere localizzazione).

L’AOP, secondo me, offre un modo di mettere fuori queste dipendenze. Un componente FormLocalization utilizzando i costrutti di linguaggio ad aspetti (strategia 3 nel tuo post, istruzioni esplicite di intercettazione) e’ in grado di "mutare" il tipo Form in uno che potremmo chiamare LocalizedForm, ove Form e LocalizedString sono dipendenti (tutto-parti). Lo stesso fa il DIP nella classe concreta. In questa visione la strategia 1 e la 3 di cui nel post sono messe insieme. Resta una classe Form riutilizzabile cioe' un dominio del problema non sporcato da problematiche della soluzione (l’applicazione concreta), una classe LocalizedString riutilizzabile cioe' un altro dominio (una virtual machine?) non sporcato da problematiche del dominio applicativo, e un componente che condensa una parte delle non-riutilizzabili problematiche applicative, non riutilizzabili perché uniche.

Due note. Invero il problema della localizzazione non e' proprio applicativo e il componente FormLocalization può essere riusato ma li ho utilizzati per coerenza con l’esempio del post. Questa peculiarità potrebbe essere dovuta alla natura "non-funzionale" del problema. Inoltre con i linguaggi ad aspetti che si stanno consolidando (alla AspectJ) non e' possibile "sostituire" le String della Form con LocalizedString. Pero' si puo' pensare ad un componente Locale che venga interrogato dopo aver letto il valore della String per avere la traduzione. Il codice di interrogazione viene realizzato con istruzioni di intercettazione nel componente FormLocalization.

L’AOP mi pare operi ad un meta-livello sulla struttura data ad un dominio tramite classi, metodi e relazioni ottenendo che questo resti slegato dalle problematiche applicative. Considero l’uso dei decoratori una soluzione vicina a questa ma che non lascia il dominio cosi' "puro". Decoratori o tipi hook risolvono il problema di modularizzare il codice di servizio (LocalizedString) ma lasciano comunque "sparpagliata" la relazione tra questo codice e chi lo richiede. Infatti nelle strategie 1 e 2 abbiamo in giro riferimenti a LocalizedString direttamente (con il tipo hook LocalizableString) o tramite decoratori. Con l’AOP si ottiene invece di modularizzare anche queste relazioni togliendole sia da Form che dalla macchina virtuale. Facendo un grafico nel primo caso disegnerei la classe Form che dipende da LocalizedString, nel secondo invece Form e LocalizedString (o Locale) sono indipendenti e allo stesso livello mentre e’ presente la classe LocalizedForm che dipende da quelle anche se in modo non convenzionale per l’OOP.

Per questi motivi considero la soluzione 3 del post (AOP o meglio AOD) una soluzione migliore delle altre due, da adottare non solo quando si ha a che fare con codice legacy ma anche quando si progetta da zero e si incappa in scattered concerns che l’OOD fatica a modellare (nella decomposizione principale).

Per finire vorrei comporre questa visione con il tuo principio "application design is language design". Direi che tramite OOA/D si costruisce un linguaggio del dominio applicativo (e del problema), tramite OOD (applicando il DIP e altri principi con strumenti ad oggetti) e AOD si deriva un linguaggio del dominio della soluzione. Poi nel main() si applica questo linguaggio per costruire la soluzione che a questo punto dovrebbe avere l’estensione di uno script. Per completezza aggiungo che alcuni sostengono che elementi "crosscutting" sono addirittura presenti nel dominio del problema e che quindi AOA/D vadano aggiunti ad OOA/D.
Michelangelo, come sempre i tuoi commenti sull'AOP sono di estremo interesse. Curiosita': la stai anche utilizzando in qualche progetto?

Solo una considerazione: hai sicuramente ragione sul "pericolo" di puntare troppo su una generalita' della VM (obliviousness del chiamato), e quindi sulle strategie 1 e 2, piuttosto che su quella del chiamante (strategia 3). Se mai avro' modo di scrivere qualcosa di piu' completo sull'argomento, cerchero' sicuramente di essere piu' chiaro questo aspetto [ :-) ]. Nel frattempo, consiglio sicuramente a tutti di leggere con attenzione quanto hai scritto.

Il tuo commento mi da' comunque una prima occasione di esplorare un argomento fortemente correlato. Gia' tempo fa osservavo come, gradualmente, stia cercando di costruirmi un buon set di euristiche di AOD (soprattutto, per quanto mi riguarda, di un blend AOD/OOD). In questo senso, la scelta tra una strategia come la (2) ed una come la (3), lasciando da parte il codice legacy che ovviamente altera la bilancia immediatamente, va fatta considerando il componente di maggior valore.

Talvolta il componente di maggior valore e' il codice applicativo della decomposizione pricipale. Talvolta e' invece la virtual machine "generalizzata" che possiamo costruire. In questo senso, possiamo vedere una forma di tensione tra generalita' (e quindi riusabilita') della VM rispetto all'obliviousness (che poi nuovamente porta a riusabilita' e chiarezza) del codice applicativo.

In effetti, nessuno dice che il codice applicativo ha, sul lungo termine, il maggior valore! Anzi, ad essere sinceri, vi sono molti casi in cui il codice applicativo ha vita breve, e nessuna chance realistica di riuso, mentre una infrastruttura (VM) generale puo' essere efficacemente impiegata in numerose altre applicazioni. In questi casi, il valore maggiore sta sul versante della VM.

Mi spiego meglio, riprendendo anche in questo caso gli esempi reali/realistici discussi sinora. Pensiamo all'esempio dell'interazione client application / web application che presentavo in un post recente. Nel caso specifico e' stato affrontato con una strategia di tipo (2). La VM e' totalmente generale e la riuseremo in numerose web page in diverse applicazioni. Tuttavia le pagine, di per se', non sono un grande asset: la probabilita' di riusarle in altre applicazioni e' molto piccola. Di conseguenza puntare su una obliviousness del chiamante e' economicamente meno interessante. Resta vero quanto dici: la relazione resta "sparpagliata" nel codice. E' altresi' vero che chi scrive quelle pagine, alla fin fine sa benissimo che quei dati arrivano dal client, ed in quest'ottica gli e' probabilmente piu' pratico dichiararlo esplicitamente "sul posto" tramite attrivuti.

Pensiamo ora alla localizzazione utilizzando la strategia (2), con attributi .NET style. Anche qui avremmo una VM generale e riusabile. Come sopra, la decorazione resta pero' "sparpagliata" nel codice.
Qui c'e' una importante distinzione da fare, che per brevita' non ho riportato nel testo originale: dipende se questo codice e' quello dei controlli (eventualmente user-defined), piu' che delle singole form.
Se spostiamo il focus dalle form ai controlli per una strategia (3), il confronto economico con (2) e' meno semplice, perche' un codice ad aspetti con strategia (3), che dichiari dei pointcuts sui controlli, costituirebbe comunque una VM relativamente generale (probabilmente da estendere quando si creano nuovi controlli). Qui si innescano pero' tutta una serie di considerazioni concrete: ad es. e' facile scrivere pointcut ed advice quando vediamo i sorgenti del chiamante, non cosi' facile se parliamo di black box (altro argomento che vedo discusso pochino in ottica AOP/AOD).

Mi fermo qui, anche perche' non e' per me molto importante arrivare a dire, fuori contesto, se e' meglio la (2) o la (3) per la localizzazione. Mi preme invece che arrivi il messaggio di design come elemento fortemente contestuale e come perno di decisioni economiche importanti. Il che mi ricorda che ieri sera pensavo ad una eresia del tipo "agility in large scale projects requires careful planning and design" :-)), ma questa e' un'altra storia.
Carlo, e’ verissimo come dici che a volte la VM e' più importante dell’applicazione che ci "gira sopra", soprattutto quando quest’ultima ha requisiti molto volatili. E le considerazioni di valore che fai mi trovano d’accordo quindi a volte la soluzione 2 o la 1 sono soluzioni ottime. Altre volte se sulla bilancia la riutilizzabilità di qualche classe pesa un po’ di più si potra’ usare la soluzione 3.

Forse nel mio post non era molto chiaro (oppure sto dicendo un’ovvieta’) ma io vedrei la soluzione 3 combinata con la 1 cioe’ non necessariamente un aspetto deve modellare il codice che fa le cose. Questo lo si può mettere in una classe (LocalizedString o Locale nell’esempio). All’aspetto resta solo il compito di modularizzare la relazione tra questa classe e il resto del codice. In questa visione potremmo anche non avere bisogno di scrivere la classe di "servizio" ma utilizzarne una fatta da qualcun altro.

Nel caso che esamini in Client Side-Augmented Web Applications se le considerazioni di valore diventassero altre allora si potrebbe pensare a generalizzare la tua soluzione: cosa succede infatti se un'applicazione client ha necessita' di chiedere alla pagina il Filename (SendClient per la pagina) e un’altra invece, su un’altra macchina, lo invia (AskClient)? Si potrebbe pensare quindi ad una classe DocumentProperties senza decoratori e ad uno o più aspetti che vengono "intessuti" con tale classe al momento della richiesta della pagina al server e che piazzano i decoratori nei punti opportuni. La VM invece manterrebbe l’implementazione che hai fatto. In questo modo la tensione tra generalità della pagina e generalità della VM si risolve completamente: entrambe sono generali. Non lo e' solo la relazione fra esse che e' stata messa fuori in un modulo (aspetto) che si puo' mettere e togliere. Sei d’accordo?

Riguardo invece alla questione del codice a me pare che se le classi applicative e quelle di servizio sono ben fatte (una chimera?) allora per scrivere gli aspetti dovrebbe bastare solo la loro interfaccia pubblica. Se non e' cosi' forse potremmo essere di fronte ad un indizio di qualche problema della decomposizione principale e ad un’occasione per ascoltare i materiali che "parlano" come dici tu.

Cio' che scrivo e' solo un parere personale benché un minimo fondato sulla letteratura (come sai Kiczales difende la doppia obliviousness) ma non esperienza. Purtroppo sviluppando quasi esclusivamente in C++ ho le mani un po’ legate sulla possibilità di fare esperienza con l’AOP in progetti reali. AspectC++ e' ancora in una fase embrionale. Fuori dal lavoro invece il tempo rimane solo per qualche lettura tecnica e qualche riflessione.
io vedrei la soluzione 3 combinata con la 1 cioe’ non necessariamente un aspetto deve modellare il codice che fa le cose.
Assolutamente giusto. Credo fosse chiaro anche nel tuo commento precedente, ma hai fatto bene a sottolinearlo.

In questo modo la tensione tra generalità della pagina e generalità della VM si risolve completamente: entrambe sono generali. Non lo e' solo la relazione fra esse che e' stata messa fuori in un modulo (aspetto) che si puo' mettere e togliere. Sei d’accordo?
Assolutamente si... in realta' in quest'ottica la tensione e' tra 3 elementi: chiamante, chiamato, aspetto di relazione. Di nuovo, ci si puo' riportare ad una questione di valore: nel caso concreto, se devo scrivere un aspetto di relazione per ogni pagina, preferisco probabilmente valutare l'idea di duplicare [parte del]la pagina :-).
Detto seriamente, un enorme vantaggio del rinunciare alla doppia obvliousness in questo caso concreto e' che non richiede alcuna conoscenza dell'AOP da parte di chi sviluppa l'applicativo web (o il client). Deve solo capire il concetto di attributo (che gli sviluppatori .NET conoscono comunque). Ma uscendo dall'esempio specifico, sicuramente ci sono casi dove il valore dell'infrastruttura e del codice applicativo sono entrambi alti, ed il miglior bilancio economico sta nello scrivere aspetti di relazione.

Riguardo invece alla questione del codice a me pare che se le classi applicative e quelle di servizio sono ben fatte (una chimera?) allora per scrivere gli aspetti dovrebbe bastare solo la loro interfaccia pubblica
Prendiamo un esempio concreto, anzi rimaniamo sull'esempio della localizzazione. Supponiamo di voler localizzare i controlli (che sono pochi / relativamente stabili) e non tutte le form di tutte le applicazioni. Dovrei scrivere degli aspetti di relazione tra i controlli ed una LocalizedString. Per fare questo deve necessariamente introdurre dei pointcut nei controlli. Dove? Se abbiamo fortuna, le stringhe vengono passate in chiaro in alcune funzioni di set, ed e' li' che possiamo intervenire. Se non e' questo il caso, e la stringa viene reperita in modo autonomo (dato un ID o che altro) non e' per niente facile (e magari nemmeno possibile) farlo guardando la sola interfaccia pubblica. C'e' anche un evidente problema di manutenzione, che gia' citavo in un altro commento in cui esprimevo i miei dubbi sul voler perseguire la doppia obliviousness ad ogni costo [letteralmente :-)]. L'alternativa e'spostare il focus dove abbiamo piu' controllo (le form). Ma questo rovina un po' il bilancio economico, e sposta uno sforzo su chi sviluppa l'applicazione, anziche' posizionarlo a livello infrastrutturale...
Non so se si tratta di un "ben fatte" Vs. "mal fatte". Qui credo che [mi] manchino proprio alcuni concetti fondamentali su cosa e' un buon design ad aspetti, non tanto per gli aspetti in se', ma per il codice su cui vogliamo poter attaccare questi aspetti (da notare che se devo progettare in modo "speciale" per facilitare il weaving di aspetti, non posso comunque dire che il chiamante e' oblivious, almeno non a design level, magari lo rimane al code level).

(come sai Kiczales difende la doppia obliviousness)
Si, ci pensavo giusto nei giorni scorsi, riflettendo su come "difendere l'obliviousness" sia un po' come "difendere l'ereditarieta'". Sbagliato :-), nel senso che dobbiamo sempre distinguere obiettivi, principi e tecniche, e valutare le conseguenze di una scelta di design nel suo contesto. Che era poi il messaggio di Alexander con i pattern (che hanno un contesto), ma anche di Schon quando dice che il materiale reagisce alle nostre manipolazioni in modo inatteso (e quindi non si puo' pensare ad un design acontestuale). Sempre giorni fa pensavo che un buon architetto del software deve comprendere rapidamente gli effetti non lineari e non localizzati delle possibili scelte. Certo, le buone teorie aiutano sempre, ma io sono molto cauto [per non dire scettico :-)] quando vedo i metodologi irrigidirsi su alcune posizioni, spesso dimenticando gli obiettivi ed i principi in favore delle tecniche (che siano di volta in volta l'ereditarieta' di sola interfaccia in stile COM, il TDD, la doppia obliviousness, e quant'altro... tutte belle cose, una volta che siamo in grado di usarle nei giusti contesti e non con... obliviousness :-))).
Concordo con la tua valutazione cauta e pragmatica (contesto-valore-conseguenze) degli strumenti di progettazione (benche' possa io sembrare un fan dell'AOP :-)

Riguardo all'esempio dei controlli non posso darti torto (per ora ;-). Il mio "dovrebbe bastare solo la loro interfaccia pubblica" era un azzardo (e lo rimane, nel senso che vorrei provarlo nel design appena mi capitera').
E riguardo al pensiero di Kiczales forse sono stato un po' impreciso (oblivious?) ma la discussione sulla obliviousness credo sia lunga e ancora in corso.

Per chi vuole e puo' (io non posso) segnalo che dal 16 al 20 luglio si tiene a Genova la seconda European Summer School on Aspect-oriented Software Development (
Michelangelo, LUNGI da me l'idea di suggerire che l'obliviousness in questione fosse la tua :-).

La conferenza AOP sarebbe proprio dietro casa :-(, peccato che abbia altri impegni...

Mi rifaro' in parte leggendo i proceedings...
Post a Comment

<< Home