giovedì 29 aprile 2010

scope quality resources time

Stasera alla serata xpug ho tentato di discutere sulle variabili scope quality resources time secondo un modello "dinamico" con riferimenti alle idee di J.b. Rainseberg e
Keith Braithwaite che individuerebbero una sorta di soglia oltre la quale la qualita' determina maggiore velocita', mentre al di sotto di essa la maggiore lentezza, causata dalla scarsa qualita', puo' rendere difficile recuperare la qualita' stessa.

Non sono sicuro di essere stato molto convincente, ma la discussione e' stata interessante.
Qui ci sono le slides.

Ecco una lista di riferimenti:
quality of non decreasing velocity: http://peripatetica xiom.blogspot. com/2009_ 05_31_archive. html
e http://www.metaprog .com/blogs/ 2009/06/the- relationship- between-xp- and-scrum- project-variable s/

speed quality barrier: http://www.jbrains. ca/permalink/ 218
(ma ho citato quello che racconta in questo video: http://www.ustream. tv/flash/ video/4722190)

cynefin framework: http://www.agilearc hitect.org/ agile/articles/ order%20and% 20unorder. asp

sufficient design: https://elearning. industriallogic. com/gh/submit? Action=PageActio n&album=blog2009&path=blog2009/ 2010/sufficientD esign&devLanguage= Java

lunedì 15 marzo 2010

Bowling kata

Qualche nota sul kata del bowling in "modalità ocp".

Una esecuzione di un generico gioco del bowling è formato da una serie di frames, ognuno dei quali è composto da un certo numero di lanci. Ogni lancio è caratterizzato dal numero di birilli abbattuti.

Dunque secondo la regola che un frame generico è composto di due lanci, la sequenza di lanci {5,5}, che significa aver abbattuto 5 birilli al primo lancio e 5 al secondo, è un frame valido, mentre la sequenza {10,1} non lo è per due ragioni: una perché viene superato il totale di 10, e l'altra perché se il primo lancio è strike, il frame non può essere composto di un secondo lancio.

Quindi ogni potenziale frame è dotato di una composizione di vincoli che consentono di distinguere se il frame è valido o meno.
Come giò detto, una regola di validità è che il totale della sequenza di lanci per frame non è superiore a 10. ed un'altra è che un frame che abbia 10 al primo lancio, non ha un secondo lancio.

Tali regole vengono indicate nel codice nel seguente modo, attraverso la tecnica dei delegates. Ho definito come Constraint una funzione: che dato un frame restituisce un boolean.



Constraint sumOfAllRollMustbeLessThanTen = (x => x.Rolls.Sum() <= 10);
Constraint ifFirstRollIsTenThanTheFrameIsOver = (x => (!(x.Rolls[0] == 10) || x.Rolls.Count == 1));
Constraint ifFirstRollIsLessThanTenThenThereIsAnotherRollInTheFrame =
(x => (!(x.Rolls[0] < 10) || x.Rolls.Count == 2));


La factory, che costruisce ogni particolare bowling iniettandovi le relative regole, è il punto di partenza del kata del bowling, e compone la variante terrestre del bowling iniettando le regole di validità attraverso la chiamata: SetConstraintForFrame() cui viene passato il Constraint (dotato di una descrizione) e dell'indice di un frame.

Ciò significa che è possibile avere constraint diversi per ogni frame.
Infatti nel bowling "terrestre" i frames che vanno da 0 a 9 rispettano gli stessi constraint descritti in alto, mentre il decimo frame ha dei vincoli diversi: può essere composto fino a tre lanci, e solo se il punteggio di ognuno di essi è pari a 10.

Ciò si descrive con i seguenti constraints:


        
Constraint sumRollsNoHigherThanThirty = x => x.Rolls.Sum() <= 30;
Constraint ifFirstRollIsTenThanThereIsAtLeastAnotherRoll = x => !(x.Rolls[0]==10)||x.Rolls.Count > 1;

Constraint ifSecondRollIsTenThenThereIsAnotherRoll =
x => (!(x.Rolls.Count > 1 && x.Rolls[1] == 10) || x.Rolls.Count == 3);


(vedere nella factory)
Notare che viene usata la regola logica:
if A then B equivalente a !A || B

Oltre a determinare la validità di ogni frame, è necessario anche un modo per determinare le
regole che servono per calcolare il punteggio ed il bonus.
Solitamente il bonus è calcolato in funzione degli altri frames.
Per esempio la regola dello Spare restituisce come bonus il valore del lancio successivo al frame attuale, mentre la regola dello Strike restituisce come bonus il valore dei due lanci successivi.

Le diverse regole per ogni frame vengono valutate in ordine, e la condizione di Break indicata nella regola indica se è necessario continuare a valutare le regole successive.
Questo per evitare, per esempio, che uno Strike venga contato anche come Spare, cosa che potrebbe essere, visto che la definizione di Strike (primi 10 birilli abbattuti), è compatibile con la definizione di Spare (la somma dei due lanci abbatte 10 birilli).

Infine vi è la regola per calcolare il punteggio per frame, al quale sarà sommato il bonus, per determinare l'effettivo punteggio totale.
Il punteggio per frame è il semplice totale dei birilli abbattuti per ogni lancio all'interno del frame.

Un dilemma ancora da sciogliere è se i test unitari debbano allocare i frames o i singoli lanci.

Nel dubbio sono stati lasciati per il momento i test in entrambe le modalità.

Per esempio c'è il seguente test che, allocando esplicitamente i frames, testa il caso di giocate tutte a punteggio zero:



[Test]
public void TestAllZeroes()
{
Frame frame = new Frame(0,0);
for (int i = 0; i < 10; i++)
{
terrestrialGame.AddFrame(frame);
}
Assert.AreEqual(0, terrestrialGame.Score());
}


mentre il seguente è l'equivalente in termini di singoli lanci:



[Test]
public void TestAllZeroesByRolling()
{
for (int i=0;i<20;i++)
{
terrestrialGame.Roll(0);
}
Assert.AreEqual(0,terrestrialGame.Score());
}




La possibilità di allocare il frame è essenziale per testare i vincoli, ma una volta esposta la funzionalità e testata, è plausibile pensare di esporre al client solo il metodo "roll" e nascondere quello che si basa sull'esporre il frame, e dunque rendere il metodo che alloca il frame da pubblico a privato.
Questo significherebbe eliminare i test che si basano sui frames (accogliendo il punto di vista che i metodi privati non debbano essere testati direttamente).

Nella variante del "bowling marziano", che consiste in tre frames, ognuno costituito fino a tre lanci, ogni strike viene premiato con il risultato dell'ultimo frame.

giovedì 4 marzo 2010

Ho messo su github del codice c# per il bowling kata in modalita' ocp.

Le regole del kata ocp non permettono di implementare una nuova feature se la base di codice non consente di farlo rispettando il principio di design open closed.

Non si tratta di un kata che richieda un solo pomodoro ma è un esercizio sfidante.

Ciao.
Tonyx

lunedì 1 febbraio 2010

Marking guesses kata

Marking guess from tonyx on Vimeo.



Questo kata in C# consiste nel risolvere il seguetne problema:

data una stringa segreta, e dato un tentativo di indovinarla,

dare come risposta una serie di 'p' per ogni lettera indovinata nella posizione giusta, seguita da una serie di 'm' per ogni lettera indovinata, ma nella posizione sbagliata.

Gli esempi sono indicati nel codice stesso, e vengono trasformati in TestCase per test unitari.

sabato 24 ottobre 2009

crash experiment in Open Closed, Liskov, Equals

In questi giorni ho scritto del codice di esempio per chiarirmi ancora certe idee sulle gerarchie tra le classi.
Ho preso un dominio semplice, quello degli interi positivi dotati di somma e sottrazione, e ne ho fatto una classe.
Il costruttore non accetta numeri negativi e dunque solleva una eccezione (runtime, per semplicità):



public Natural(int value) {

if (value<0) {
throw new RuntimeException("non vale il negativo");
this.value=value;
}
}





(In alternativa si sarebbe potuto ottenere, in congiunzione con un framework di design by contract, lo stesso effetto dell'eccezione per valori negativi, tramite una precondizione tipo:
@Pre("@value>=0"))

Nei prossimi esperimenti mi ripropongo di approfondire questo punto, selezionando un framework per il design by contract.

Essendo le istanze immutabili, allora ridefinisco anche la equals.
In pratica l'effetto di ridefinire la equals è che due istanze che confrontate restituiscono true vengono considerate come se fosse lo stesso oggetto da varie api di java, come quelle che gestiscono Set e mappe.

Cioè se ridefinisco la equals allora, aggiungendo ad un set vuoto due oggetti istanziati entrambi allo stesso modo (new Natural(1)), la numerosità del set sarà uno, non due.

Se l'oggetto fosse mutabile, ovvero potesse cambiare stato dopo essere istanziato, allora la ridefinizione della equals non va fatta, in quanto possono succedere stranezze, per esempio: un oggetto può venir aggiunto ad un insieme, cambiare stato e misteriosamente sparire virtualmente dall'insieme stesso.

Tra tutte le cose misteriose che con il codice possono succedere, questa è talmente bizzarra che si desidera evitarlo, e dunque la ridefinizione della equals per i soli immutable va accettata come regola. Fare diversamente è un tabù.

Quindi per le classi immutabili va ridefinito il metodo equals (e hashcode), mentre per le classi mutabili no.

Tornando alla classe Natural, ora pongo una questione.

Come deve essere una implementazione conforme al principio Open closed?

Più in dettaglio diciamo che il dominio verrà esteso, e vogliamo allo stesso modo che questo determini il dover "aggiungere del nuovo codice", in conformità a questa estension, ma non "modificare codice esistente".

L'estensione è che bisogna descrivere anche i numeri negativi.

Ribadendo che non posso fare modifiche (per quanto semplici possano essere) ma solo estensioni, allora posso per esempio fare una sottoclasse, il cui costruttore non solleva più l'eccezione se istanziata con un negativo, cioè come segue:



public Relative(int value) {
this.value = value;
}



(Per motivi di sintassi java la classe padre deve avere un costruttore a zero argomenti che serve solo alle sottoclassi, e quindi diciamo che è stato già definito in previsione di estensioni, come protected)

Ancora una volta, in termini di precondizioni, si tratta dell'equivalente del rilassare la precondizione della classe padre (Natural) da restrittiva @Pre("@value>=0"), a meno restrittiva
eliminando semplicemente il vincolo value>0.

(Questo è coerente, tra l'altro, con le regole del dbc (design by contract), per cui le precondizioni in ereditarietà possono essere meno restritive, e non più restrittive, mentre il viceversa vale per le eventuali post-condizioni).

Un'altra precondizione che viene rilassata è quella relativa alla sottrazione, che per i naturali è valida se il sottraendo è minore o uguale al diminuendo, mentre tra i relativi non c'è questo vincolo.

A questo punto si pone il problema che abbiamo due domini regolati da due classi, ma che presentano oggetti assimililabili tra loro:
Natural è padre di Relative, e, insiemisticamente parlando, ne è un sottoinsieme.
Le operazioni di somma e sottrazione sui Natural, continuano ad essere valide per i Relative, con esiti che sono considerati uguali, dunque viene rispettato il principio di sostituibilità.

Dovremmo anche aspettarci che la equals possa ammettere con confronto altrettanto coerente tra istanza di Natural e istanze di Relative?

Secondo me sì per due ragioni. Uno è il principio di fare la scelta meno sorprendente. Sarebbe piuttosto sorprendente che l'istanza new Natural(1) e new Relative(1) vengano considerate diverse tra loro, visto che figurativamente parliamo di insiemi uno sottoinsieme dell'altro, dunque condividono alcuni elementi in comune che dovrebbero continuare ad essere gli stessi, non importa quale sia la classe concreta a cui appartengono.

Un altro motivo è reltivo alla definizione di sostituibilità. Devo poter sostituire, (ad una istanza della classe padre una opportuna istanza della classe figlia) e aspettarmi lo stesso risultato, per qualsiasi codice, e io per qualsiasi codice intendo anche la equals ovvero

(new Natural(1)).equals(new Relative(1)) restituisce true perchè lo fa anche
(new Natural(1)).equals(new Natural(1)).

Questo non è possibile farlo usando l'implementazione della equals basata sulla "getClass()" , ma è invece possibile usando quella basata sulla "instanceof".

Tuttavia questa implementazione potenzialmente può portare a risultati inconsistenti, per particolari classi estese (non in questo caso di estensione da Natural a Rational, comunque).

Per evitare questo ulteriore problema invece si può usare l'implementazione proposta nel seguente articolo: implementig equals() to allow slice comparison.

In conclusione: condizione necessaria affinché una classe che definisce oggetti non mutabili rispetti il principio open closed è che adotti la equals che permetta il "confronto a slices".

Una condizione secondaria, legata a questioni sintattiche di java, è che questa definisca un costruttore vuoto, a zero argomenti, di visibilità protected o superiore.


Note. Non posso dire di averli riletti recentemente, ma gli articoli di riferimento su questo argomento sono i seguenti

principio open closed: www.objectmentor.com/resources/articles/ocp.pdf
equals: http://www.artima.com/weblogs/viewpost.jsp?thread=4744
equals: http://www.artima.com/intv/bloch17.html
principio di sostituibilità di liskov: http://www.objectmentor.com/resources/articles/lsp.pdf
equals basata su confronto a slices (mixed type) : http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html

sabato 13 giugno 2009

Imparare lingue on line.

Per l'apprendimento delle lingue ci sono sistemi interessanti, basati su giochi, immersione dinamica: una serie di esempi con suoni, frasi ed immagini, su cui esercitarsi, memorizzare, giocare con quiz a risposta multipla. Flashcards, dynamic immersion (Rosetta Stone), e livemocha.com ne sono degli esempi.

Non spiegano la grammatica. L'idea è di imparare in modo intuitivo ed immersivo, come fanno i bambini e le persone che vivono in un paese straniero o interagiscono con stranieri.
Per riprodurre questo approccio in modo analogo, sono necessari una certa quantità di esempi, un rapido feedback, l'interattività. Per questo ci vuole uno specifico software, i riproduttori mp3, risorse on line.

Per come ho visto io ci sono dei limiti, e spazio per miglioramenti.
E' utile riconoscere una figura con una persona assetata e poi imparare che la frase associata è "lui vuole qualcosa da bere", ma ci sono tante correlazioni che questa frase ha con concetti che una persona che sa la lingua può associare, e che la carta stessa da sola non può fornire, e di conseguenza non sono fornite a chi sta imparando.

Queste correlazioni sono relative a carte simili dove è presente il verbo volere, i pronomi personali, e così via.

Se queste informazioni sono disponibili al sistema (per esempio sotto forma di tag) a quel punto le si possono presentare in sequenze dipendenti da questi tags.

Le carte presentate tradizionalmente invece, per quanto ho constatato finora (per esempio su livemocha), rispettano sequenze di similitudine solo a livello "monodimensionale": la carta che mostra uno che ha sete sta nel gruppo della carta che mostra uno che ha fame, ma ci sono altri raggruppamenti che hanno senso per altre "dimensioni", come carte che mostrino le varie coniugazioni e tempi del verbo "volere".

Se si usasseri specifici tag avremo che in quella carta ci sono tai tag tipo "bevanda", "pronome personale", il tag "nominativo" (soggetto) "avere", "presente".

A questo punto se il sistema dovesse decidere di proporre carte sulla base di tag specifici, potrebbe iniziare a mostrare frasi non nella sequenza prestabilita di quel gruppo ("bere"/"mangiare") ma nella sequenza relativa al tag o ai tag scelti (esempio: io ho fame, tu hai ..., egli ha ...) se i tag sono i pronomi personali, ed il verbo avere.
La decisione di presentare una sequenza piuttosto che un'altra dovrebbe avvenire sulla base di punteggi (il sistema pensa che hai problemi con i pronomi personali e dunque ti propone carte taggate "pronomi").

Questioni correlate sono il fatto che i tag sono diversi a seconda delle lingue: ci sono lingue che hanno i casi, lingue che hanno più forme verbali, e così via. Quindi ogni lingua avrebbe diversi tipi di tags (questione non banale, visto che le carte, per semplicitò, in Rosetta Stone come in livemocha, sono le stesse per tutte le lingue).

Poi c'è il problema dele frasi sinonime "lui ha sete" non esiste nella stessa forma letterale in altre lingue. Russo: "lui vuole bere" (он хочет пить). Inglese: "lui è assetato" ("he is thirsty").
Questa informazione non è presente nelle carte di Livemocha come quella di Rosetta Stone.

Prima credo che qualcuno farà un improvement su sistema di apprendimento basato su questi concetti, ma nel frattempo è ancora troppo presto per decidere di mettere da parte libri, grammatiche, eserciziari, ed integrarli con questi sistemi on line (livemocha, lingq, per esempio).

Non trascurare poi lo stesso google, i libri in formato pdf per fare ricerche rapide, e magari un wiki per prendere appunti (es. tiddlywiki)

martedì 26 maggio 2009

brevi note sul principio Open Closed e quello di sostituibilità

Open closed: open to extensions, closed to modifications.
Se una parte di un sistema non necessita di modifiche, ma semplicemente di nuove aggiunte a fronte di possibili evoluzioni del sistema stesso, allora si sta rispettando il principio open closed.
In realtà non esiste un "open closed" per tutte le stagioni, ovvero valido per ogni possibile evoluzione. Ci sarà sempre qualche evoluzione che richieda un cambiamento.

Più pragmaticamente c'è il concetto di "chiusura strategica" che vorrebbe dire poter rispettare questo principio rispetto a quelle evoluzioni che più verosimilmente si verificheranno (o quelle che ci interessano di più).

Principio di sostituzione di Liskov:
se il comportamento di tutti (*) i programmi che usano una certa classe rimarrà invariato sostituendo a quella classe una sua sottoclasse, allora nel design classe->sottoclasse viene rispettato il principio di sostituzione di Liskov.
Quindi se una certa sottoclasse farà altre cose, oltre a quelle che fa la classe base, oppure farà le stesse cose della classe base ma in modo diverso, allora non occorrerà fare modifiche a tutti i metodi che usano la classe base affinche' si prevengano comportamenti "strani".

In questo senso il principio di sostituzione di Liskov può essere considerato un caso particolare del principio Open Closed, perché la classe base è "chiusa" rispetto a modifiche dovute alla creazione di sue sottoclassi.

In Eiffell la questione viene affrontata attraverso il design by contract.
Nel t.d.d. una tecnica semplice e' quella di applicare alle sottoclassi gli stessi test che vengono applicati alle classi base.

Entrambi questi principi direi che discendono dal più astratto principio del "least astonishment" "http://en.wikipedia.org/wiki/Principle_of_least_astonishment".

(*) nota. Non proprio tutti i programmi, altrimenti dall'esistenza di programmi che hanno un comportamento che dipende esplicitamente da un controllo di appartenenza di classe su un oggetto (if (myInstance.getClass()) { ... }, si dedurrebbe che l'insieme delle sottoclassi che rispettano il principio sarebbe vuoto.

Riferimento principale:
http://butunclebob. com/ArticleS. UncleBob. PrinciplesOfOod

mercoledì 20 maggio 2009

Deep dynamics

Ad un incontro con xp ug di Milano ho parlato per un'oretta di quanto mi ricordavo di "Deep Dynamics of agile teams".

Gli argomenti sono stati:

identità individuale

"mainstream flow, cambiamenti/turbolenze", metafora del fiume, e degli affluenti,
conversazioni difficili,
B.A.T.N.A.: best alternative to negotiation agreement,

intento e impatto di un messaggio

"yes, and..." vs. "yes, but...",

gestire la polarizzazione tra due tesi contrapposte, (*)
conflitto, soluzioni win win. (cooperazione rispetto a compromesso o resa/vittoria)

ribaltare una "accusa" ("tu sei X". "Se per X intendi .... allora si' d'accordo con te")
"temperatura" come stato: confort, un-confort, panic

learning culture vs blaming culture

mindset preconvenzionale, convenzionale, postconvenzionale.
social rank, legitimate rank, personal rank.

Mi è stato chiesto qualche riferimento bibliografico, e sono rimasto spiazzato.
Mi sono ricordato di “Project Retrospectives” di Norman Kerth, ma in realtà era faceva parte dei testi consigliati in un altro corso di Joseph, quello di Scrum. Forse alla fine una bibliografia non ci fu data. Si tratta in effetti di un mix di argomenti mutuati dalla psicologia applicati alle interazioni interne ad un team.

"Difficult Conversations"

Ho pensato a quali libri io avrei considerato vagamente correlati all'argomento, e mi sono venuti in mente:
"Calcoli Morali - Lazlo Mero "
"Nexus. Perché la natura, la società, l'economia, la comunicazione funzionano allo stesso modo - Buchanan Mark"
"Creatività e pensiero laterale -E. De Bono"


Avrei anche aggiunto, ma un po' più, alla lontana
"Verso un'ecologia della mente. G. Bateson"
"La società della mente - Marvin Minsky"

lunedì 18 agosto 2008

Il cavaliere oscuro e il dilemma del prigioniero

Attenzione, questo post contiene spoiler del film "Il cavaliere oscuro".

In una scena del film "il cavaliere oscuro" si presenta una variante del dilemma del prigioniero.

Un gruppo di persone si trova in una nave. Esse hanno accesso ad un detonatore che puo' far saltare in aria una seconda nave nella quale vi sono altre persone, ed un altro detonatore, che può far saltare in aria la prima nave.
Se nessuno dei due detonatori verra' premuto, il Joker dice che fara' saltare in aria entrambe le navi.

La cosa si puo' schematizzare nel seguento modo:

ci sono un ostaggio A in una nave ed un ostaggio B in un'altra nave.

L'ostaggio A puo' raggiungere in un certo tempo T_A il detonatore in grado di far saltare in aria la nave su cui si trova l'ostaggio B.
l'ostaggio B puo' raggiungere in un certo tempo T_B il detonatore in grado di far saltare in aria la nave su cui si trova l'ostaggio A.
Ad entrambi e' stato fatta la minaccia che se ne' l'uno ne' l'altro premera' il detonatore entro una certa ora, allora saranno uccisi entrambi.

Non e' noto se il tempo per raggiungere il detonatore sia piu' basso per A o per B, quindi la probabilita' che ci metta piu' tempo A o piu' tempo B sono da considerare pari al 50%.

Se l'obiettivo e' solo di salvare il massimo numero di vite umane (una o nessuna), la strategia migliore e' quella di raggiungere il detonatore e premerlo. In questo caso almeno uno dei due si salva, cioe' quello che arriva prima al detonatore.

Ma possono influire anche altri fattori, come il non essere in grado di risolvere il dilemma morale che la scelta comporta, o anche il considerare la possibilita' che il Joker non mettera' in atto la sua minaccia, rendendo un sacrificio inutile l'aver premuto il detonatore.

Se si fosse sicuri che anche l'altro ostaggio ha deciso di premerlo, allora si puo' legittimare la scelta di farlo a propria volta, per "difendersi".

Abbiamo un mix dei seguenti principi, a determinare la scelta:
premere il detonatore e' accettabile in caso di "leggittima difesa preventiva", cioe' se si e' sufficientemente sicuri che l'altro ha deciso di farlo a sua volta.
Premerlo può comportare un sacrificio inutile, se il Joker non mettera' in atto la sua minaccia di far saltare in aria entrambi qualora nessuno dei due dovesse premerlo.

Al di là del Joker, ponendo che l'unica motivazione per premere il bottone è di difendersi preventivamente.


A si dirige verso il detonatore e lo raggiunge.
A è ancora vivo, quindi pensa che B non ha ancora raggiunto il detonatore oppure ha deciso di non premerlo.

C'è la possibilità che B ha deciso di non premere il detonatore, e allora A decide di non farlo a sua volta.

Questa possibilità con un ragionamento fallace può essere considerata al 50%, come per esempio.

Se B avesse voluto premere il detonatore, avrebbe avuto tempo D_B, per premerlo, e la probabilita' che D_B sia inferiore D_A e' stimata al 50%.
A non e' saltato in aria, e quindi o B non ha ancora raggiunto il detonatore (50% di probabilita') oppure ha deciso di non premerlo (con la restante probabilita' del 50%).

Se B fa lo stesso ragionamento nessuno dei due lo premera'.

Il ragionamento pero' per essere completo avrebbe bisogno di ulteriori informazioni, cioe' le probabilita' a priori, e l'uso della regola di Bayes, che cambiano considerevolmente questa stima del 50%.
Poniamo per esempio che la probabilita' a priori che B voglia premere il detonatore sia del 90%, abbiamo i seguenti casi:

P(B vuole premere il detonatore) = 0.9
P(B non vuole vuole premere il detonatore) = 0.1
P(B preme il detonatore) = P(B vuole premerlo e D_B<D_A) = P(B vuole premerlo)*P(D_B<D_A) = 0.45
P(B non preme il detonatore) = P(B vuole premerlo e D_B>D_A) oppure P(non vuole premerlo) = 0.45+0.10

A arriva vivo al detonatore, e vuole sapere questo fatto quanto cambiano le cose, cioe' quanto vale la probabilita' che B vuole premere il detonatore dato che A e' arrivato vivo al detonatore.

La probabilita' dell'evento contrario (che B non vuole premere il detonatore dato che A e' arrivato vivo) e':
P(B non vuole premere il detonatore | A arrivato vivo al detonatore) = P(A arrivato vivo al detonatore | B non vuole premere) P(B non vuole premere)
P(A arrivato vivo al detonatore | B non vuole premere)* P(B non vuole premere) + P (A vivo davanti al detonatore | B vuole premere)*P(B vuole premere) = (1*0.1)/(0.1 + 0.5*0.9) = 0.18

P(B vuole premere | A arrivato vivo al detonatore) = 0.82.

Tutto dipende dalla probabilita' a priori che P voglia premere il detonatore, che e' un problema di mindset di attidudine mentale/morale che A ha di B.

Infatti, proprio nel film, si mostra chiaramente che questi ipotetici sistemi di riferimento morale sono presumibilmente diversi:
in A vi sono cittadini comuni, in B dei carcerati.

riferimenti:
dilemma del prigioniero: http://it.wikipedia.org/wiki/Dilemma_del_prigioniero,
regola di Bayes: http://it.wikipedia.org/wiki/Teorema_di_Bayes,
si e' vagamente accennato a problemi di decisione che possono essere chiamati dilemmi morali. Per saperne di piu': http://en.wikipedia.org/wiki/Trolley_problem
Altro blog: http://www.quantitativepeace.com/blog/2008/07/the-dark-knight.html


sabato 29 marzo 2008

politiche economiche e fiscali for dummies

Della serie: "Io sono di parte, ma le cifre no!"
Bel lavoro!

venerdì 22 febbraio 2008

yaari spam (capitato anche a me)

Mi sono iscritto a questo sito di social network (yaari) causa di un "invito" arrivatomi via mail presumibilmente da una persona che conosco.
Effettivamente i dati coincidevano quasi.
Errore: dopo che mi sono iscritto, sono partiti altrettanti falsi inviti a mio nome verso indirizzi presi dalla mia rubrica (tra cui un mio secondo account).

A quel punto mi sono disiscritto (o almeno spero) ed ho invitato i miei amici a cestinare questo tipo di inviti.

Probabilmente anche io sono incappato nel problema quanto descritto da questo articolo.

Ovviamente cercano di fare piu' utenti possibili, ma questa politica scredita i siti di social network (anche quelli piu' seri), e poi non so fino a che punto funzioni davvero.

Immagino che le segnalazioni anti-spam ed anti-abuse che ne seguono finiscano per far aggiornare i filtri anti-spam dei vari providers, in modo da filtrare gli inviti provenienti da quel sito, compresi quelli leciti (volontari).

(p.s. pero' una cosa buona come conseguenza e' capitata: l'invito involontario e' arrivato a persone che non sentivo da anni, le quali, pur declinando, hanno sentito il bisogno di rispondere, insomma ho avuto occasione per riallacciare dei contatti ;-) )

lunedì 18 febbraio 2008

Bash: come capire quale .jar contiene una certa classe

Se sei nella directory contenente i jar da cercare:

for i in `ls *.jar`; do echo $i; jar -tvf | grep eventuale.package.NomeCasse; done;

Se i jar possono essere in diverse sottodirectory a partire dalla directory corrente:

for i in `find . -name | egrep ".*\.jar$"`; do echo $i; jar -tvf | grep eventuale.package.NomeCasse; done;


(Non ho provato su Windows)

martedì 12 febbraio 2008

martedì 5 febbraio 2008

Torneo Refactoring parte 2

Sempre a riguardo della prova relativa al torneo refactoring, provo a continuare con la mia proposta di soluzione, iniziata nel post precedente.

la prova03 evidenzia che nella classe printSlip la evaluate non sarebbe testabile perché usa lo standard output:
public void evaluate(Object aResource) {
System.out.println(((Resource)aResource).name());
System.out.println(((Resource)aResource).salary());
}
incidentalmente questa implementazione corrisponde al layout sintetico, descritto più avanti, che ha output tipo:
"Francesco\n1000.00\n"

Esiste anche un layout più dettagliato, che viene chiesto di implementare:
Il layout dettagliato della busta paga di una risorsa ha il seguente formato: "Nome: , Salario: \n". Per esempio, per Francesco con salario 1000 euro, il sistema produrrà: "Nome: Francesco, Salario: 1000.00\n"

Abbiamo due obiettivi:
1)rendere testabile la printSlip,
2)poter gestire più di un layout.

Per il n.1, astraggo sulla modalità di gestione dell'output, quindi la "stampa" userà un generico OutputStreamer (interfaccia che creiamo poi, che prescrive un unico metodo "print") e posso usare un mio OutputStreamer "fittizio" (mock) solo per i test.

Per il n.2, rendo astratta la classe PrintSlip stessa, delegandone la concreta implementazione della evaluate (sintetica o analitica), che implementeranno i due di layout:


public abstract class PrintSlip implements Block {

protected OutputStreamer outputStreamer = new OutputStreamer()
{
public void print(Object object) {
System.out.print(object);
}
};

public void setOutputStreamer(OutputStreamer outputStreamer) {
this.outputStreamer=outputStreamer;
}

public OutputStreamer getOutputStreamer() {
return outputStreamer;
}

public abstract void evaluate(Object aResource);

}


Implementazine "sintetica":


public class PrintSlipImplSyntetic extends PrintSlip {
public void evaluate(Object aResource) {
outputStreamer.print(((Resource)aResource).name());
outputStreamer.print("\n");
outputStreamer.print(((Resource)aResource).salary());
}
}



Implementazione "analitica":



public class PrintSlipAnalitic extends PrintSlip{
public void evaluate(Object aResource) {
outputStreamer.print("Nome:"+((Resource)aResource).name()+",Salario:"+((Resource)aResource).salary()+"\n");
}
}




Per testare queste due novità mocko la evaluate in modo che l'output sia diretto in una stringa:


@Test
public void testPrintSlip() {
Contract nov1 = new Contract(nov1st2005());

PrintSlip printSlip = new PrintSlipImplSyntetic();

printSlip.setOutputStreamer(
new OutputStreamer() {
public void print(Object object) {
result += object;
}
}
);

MockedEmployee employee1 = new MockedEmployee("Francesco", nov1);
employee1.setPrintSlip(printSlip);
employee1.printSlip();
String results = printSlip.getOutputStreamer().getResult();
assertEquals("Francesco\n1024.89",results);

}



@Test
public void testPrintSlipAnaliticWithFactory() {
Contract nov1 = new Contract(nov1st2005());

PrintSlipFactory factory = new PrintSlipFactory();
factory.setCurrentLayout(PrintSlipFactory.ANALYTIC);
PrintSlip printSlip = factory.getPrintSlipWithCurrentLayout();

printSlip.setOutputStreamer(
new OutputStreamer() {
public void print(Object object) {
result += object;
}
}
);

MockedEmployee employee1 = new MockedEmployee("Francesco", nov1);
employee1.setPrintSlip(printSlip);
employee1.printSlip();
String results = printSlip.getOutputStreamer().getResult();
assertEquals(results, "Nome:Francesco,Salario:1024.89\n");
}






Btw sia in questa prova (prova03) che in quella precedente sembra che si suggerisca di incapsulare le PrintSlip e InForce per rendere alcuni oggetti meno... "tristi"

Io le ho spostate entrambe in Resource:


abstract public class Resource {
protected Block printSlip = new PrintSlipImplSyntetic();

public boolean isInForce()
{
InForce inForce = new InForce();
return inForce.is(this);
}

public void setPrintSlip(PrintSlip printSlip)
{
this.printSlip=printSlip;
}

public Block getPrintSlip()
{
return printSlip;
}

public Resource(String name, Contract contract) {
_name = name;
_contract = contract;
}

abstract public double salary();

public Contract lastContract() {
// Semplifichiamo :) la ricerca dell'ultimo contratto
return _contract;
}

public String name() {
return _name;
}

public String toString() {
return _name + ":" + _contract + ".";
}

private String _name;
private Contract _contract;

public void printSlip() {
printSlip.evaluate(this);
}

}


In questo modo il test precedente può diviene:


@Test
public void testPrintSlipDefaultWithFactory() {
Contract nov1 = new Contract(nov1st2005());

PrintSlipFactory factory = new PrintSlipFactory();
factory.setCurrentLayout(PrintSlipFactory.SINTETIC);
PrintSlip printSlip = factory.getPrintSlipWithCurrentLayout();

printSlip.setOutputStreamer(
new OutputStreamer() {
public void print(Object object) {
result += object;
}
}
);

MockedEmployee employee1 = new MockedEmployee("Francesco", nov1);
employee1.setPrintSlip(printSlip);
employee1.printSlip();
String results = printSlip.getOutputStreamer().getResult();
assertEquals(results, "Francesco\n1024.89");

}


posto, comunque, che abbia preso come decisione di adottare una factory che consenta di settare un layout, e restituire poi la printSlip relativa al layout così settato:



public class PrintSlipFactory {
public static String SINTETIC="SINTETIC";
public static String ANALYTIC="ANALYTIC";
private OutputStreamer currentOutputStreamer= new OutputStreamer() {
public void print(Object object) {
System.out.print(object);
}
};

private String currentLayout="SINTETIC";

private static HashMap mapLayouts=new HashMap();
static {
mapLayouts.put("SINTETIC",PrintSlipImplSyntetic.class);
mapLayouts.put("ANALYTIC",PrintSlipAnalitic.class);
}

public void setCurrentLayout(String layout) {
this.currentLayout = layout;
}

public void setCurrentOutputStreamer(OutputStreamer outputStreamer)
{
this.currentOutputStreamer=outputStreamer;
}

public PrintSlip getPrintSlipWithCurrentLayout() {
try {
PrintSlip toReturn = (PrintSlip)((Class)mapLayouts.get(currentLayout)).newInstance();
toReturn.setOutputStreamer(currentOutputStreamer);
return toReturn;
} catch (Exception e)
{
// unable to set layout
}
// default
return new PrintSlipImplSyntetic();
}
}




Ultima cosa, in relazione alla gestione dell'output su file di testo, penso ad una logica che sfrutti l'outputStreamer pensato per gestire il mock, e faccio un test allo scopo per vedere se può funzionare:


@Test
public void testPrintSlipAnaliticWithFactoryFileTextOutputStreamer() {
Contract nov1 = new Contract(nov1st2005());

PrintSlipFactory factory = new PrintSlipFactory();
factory.setCurrentLayout(PrintSlipFactory.ANALYTIC);
PrintSlip printSlip = factory.getPrintSlipWithCurrentLayout();

final MockedEmployee employee1 = new MockedEmployee("Francesco", nov1);

printSlip.setOutputStreamer(
new OutputStreamer() {
public void print(Object object) {
try {
File outputFile = new File("./" + employee1.name());
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(((String) object).getBytes());
outputStream.flush();
outputStream.close();
}
catch (Exception e) {
fail(e.toString());
}
}
}
);

employee1.setPrintSlip(printSlip);
employee1.printSlip();

File file = new File("./" + employee1.name());
assertTrue(file.exists());
file.delete();

}




Infine arricchisco la classe department (che contiene una collezione di cui esegue la printSlip rispetto a tute le risorse in forza), in modo che questa stessa classe essa stessa possa gestire i layout e l'OutputStreamer:





public class Department {

private PrintSlipFactory printSlipFactory = new PrintSlipFactory();

public void setPrintSlipLayout(String layout) {
printSlipFactory.setCurrentLayout(layout);
}

public void setPrintSlipOutputStreamer(OutputStreamer outputStreamer) {
printSlipFactory.setCurrentOutputStreamer(outputStreamer);
}

public Department(List resources){
_resources = resources;
}

public void printSlips() {
new OrderedCollection(_resources).select(new InForce()).forEachDo(printSlipFactory.getPrintSlipWithCurrentLayout());
}

private List _resources;
}




Mancano la parte R5 ed R6 della prova03, ed infine la prova4.

Al prossimo post, magari.

Bye.

sabato 2 febbraio 2008

Torneo Refactoring

E' partito un torneo di refactoring.

Non partecipando, comunque ho deciso di affrontare il problema, per il momento commento come avrei affrontato la prova01:

Il codice e' versionato nel repos. aziendale.

prima di fare il refactoring richiesto, che chiede di sfruttare la somiglianza tra evaluate e forEachDo, creo dei test appositi per essi, e poi rifattorizzo, e verifico che i test passano anche dopo il refactoring.

Per testare la forEachDo:
mi appoggio ad una implementazione di Block che implementa la evaluate concatenando i toString degli oggetti processati in una unica stringa globale.
Se applico la evaluate con questa implementazione di Block, ad una collezione contenente "first" e "second" mi aspetto che dopo il test la stringa globale abbia concatenato "first" e "second".

public class BlockImplForTest implements Block {
public static String references="";
public static void reset() {
references="";
}

public void evaluate(Object object)
{
references+=object.toString();
}
}

Il test e' il seguente:

@Test
public void testSelect() {
OrderedCollection col = new OrderedCollection();
col.add("first");
col.add("second");

Block stringRefAppender=getFreshBlockImplForTest();
col.forEachDo(stringRefAppender);

assertEquals(BlockImplForTest.references,"first"+"second");
assertFalse(BlockImplForTest.references.equals("wrong"));

}

Per rendere piu' evidente che Il Block che uso deve essere "fresco", lo reperisco tramite questo metodo che fa anche un reset dello stato:

BlockImplForTest getFreshBlockImplForTest() {
BlockImplForTest.reset();
return new BlockImplForTest();
}

Forse, come design, e' discutibile aver usato una variabile globale, ma allo scopo del test va piu' che bene.

Il test del select crea una classe anonima che implementa il PredicateBlock in modo che che la is restituisca true solamente se la stringa vale "second".
In questo modo, mi aspetto che la lista filtrata attraverso questo PredicateBlock contenga solamente la stringa second. Sfrutto parte della logica del precedente test per scandagliare la collezione restituita e verificare che contenga solamente "second".

@Test
public void testSelector() {
OrderedCollection col = new OrderedCollection();
col.add("first");
col.add("second");

Block stringRefAppender=getFreshBlockImplForTest();

assertEquals("",BlockImplForTest.references);
PredicateBlock getOnlySecond= new PredicateBlock()
{
public boolean is(Object object) {
return "second".equals(object);
}

};

OrderedCollection ordCol = col.select(getOnlySecond);
ordCol.forEachDo(stringRefAppender);
assertEquals("second",BlockImplForTest.references);

}

Il test ci assicura che l'attuale implementazione dei metodi di OrderCollection fanno quello che ci aspettiamo, ed ora la si rifattorizza cercando di sfruttare la somiglianza tra forEachDo e select.
Entrambe eseguono la scansione della collezione, solo che una esegue una certa operazione, e lo fa su tutti gli elementi, senza restituire nulla, l'altra restituisce una sottocollezione di tutti i membri della collezione che soddisfano un certo predicato.

Ragionando un attimo, dal punto di vista logico possono essere due casi particolari di una unica operazione che scandisce gli elementi , valuta il predicato, esegue l'operazione ed (eventualmente) restituisce gli elementi processati.

Il caso particolare della forEachDo e' che il predicato restituisce sempre true, che non ci interessa utilizzare il valore restituito, mentre il caso particolare della select e' che la evaluate non esegue nulla.

La rifattorizziamo dunque creando due implementazioni di Block e PredicateBlock in accordo con questi due casi particolari:

1) Creiamo il Block e il PredicateBlock corrispondente ai casi particolari appena discusi:

private static final PredicateBlock passesAll =
new PredicateBlock() {
public boolean is(Object object) {
return true;
}
};

private static final Block neuterBlock =
new Block() {
public void evaluate(Object object) { }
};


2) Aggiungiamo un metodo "evaluate and select":

private OrderedCollection evaluateAndSelect(PredicateBlock pb, Block b)
{
OrderedCollection result = new OrderedCollection();
Iterator iterator = _items.iterator();
while(iterator.hasNext()) {
Object object = iterator.next();
if (pb.is(object)) {
b.evaluate(object);
result.add(object);
}
}
return result;
}

3) Trasformiamo la ForEach e la Select in modo che siano appunto casi particolari dell'utilizzo di evaluateAndSelect sfruttando le implementazioni "neutre" di Block ed PredicateBlock:

public OrderedCollection select(PredicateBlock aBlock) {
return evaluateAndSelect(aBlock,neuterBlock);
}

public void forEachDo(Block aBlock) {
evaluateAndSelect(passesAll,aBlock);
}

sabato 29 dicembre 2007

Attributi per dichiarare le relazioni tra diverse rappresentazioni concrete di un tipo

Nel testo "Structure and Interpratations of Computer Programs", c'è una sezione che tratta di rappresentazioni multiple per dati astratti, con l'esempio delle due possibili rappresentazioni dei numeri complessi: forma polare e forma cartesiana, o rettangolare.

Vi è una corrispondenza biunivoca tra le due rappresentazioni, e la conversione da una rappresentazione all'altra è possibile sfruttando le seguenti relazioni:





Ho creato un progettino (in java) che investiga su questo argomento.

Una factory restituisce una rappresentazione concreta piuttosto che un'altra e lo fa reperendo i mapper che associano una rappresentazione ad un'altra.

Ogni costruttore di ogni implementazione, in modo dichiarativo, ovvero tramite attributi, mostra di essere mappabile in altri costruttori. Sa come trasformare la n-pla di parametri di inizializzazione nella equivalente n-pla usata da un altro costruttore e/o da un'altra implementazione.

Un complesso è definito come segue.

public interface ComplexNumber {
public double getReal();
public double getImg();
}

Il come costruirlo lo gestiremo nella factory.

Abbiamo le due seguenti implementazioni, Cartesiana e Polare:
public class ComplexNumberCartesian implements ComplexNumber {
protected double real;
protected double img;

@instanceConverter(instanceConverterMap = CartesianToPolarMapper.class)
public ComplexNumberCartesian(double real, double img) {
this.real=real;
this.img = img;
}

public double getReal() {
return real;
}

public double getImg() {
return img;
}

...


}





Polare:

public class ComplexNumberPolar implements ComplexNumber {
protected double magnitude;
protected Angle angle;

protected double real;
protected double img;

@instanceConverter (instanceConverterMap = PolarToCartesianMapper.class)
{
this.magnitude = magnitude;
this.angle = angle;

this.real = magnitude*Math.cos(angle.getValue());
this.img = magnitude*Math.sin(angle.getValue());
}

public double getReal() {
return real;
}

public double getImg() {
return magnitude*Math.sin(angle.getValue());
}


public boolean equals(Object object)
{
...
}

public int hashCode()
{

}
public String toString()
{
}
}

Nel costruttore abbiamo l'attributo @instanceConverter, che indica a sua volta la classe mapper, che associa a (real, img), l'equivalente coppia (ampiezza,angolo).

Ecco la definizione di questa annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
public @interface instanceConverter {
Class instanceConverterMap() default IdentityToObject.class;
}

Identity è il caso banale appunto dell'identità.

Noi sfruttiamo il mapper dalla forma cartesiana (o rettangolare) a quella polare:

public class CartesianToPolarMapper implements WrapperToObject{
public Wrapper getWrapperForClassConstructorWithParameters(
Class targetClass,
Class targetParamsTypes[],
Class[] originParamsTypes)
{

if (targetClass.equals(ComplexNumberPolar.class)) {
return new CartesianToPolar();
}
throw new RuntimeException("wrapper not defined for the target class");
}
}

In teoria la conversione specifica dipende anche dal tipo di costruttore della classe target invocato, e quindi si specifica anche il targetParamsType che è un array di classi (che consente di reperire il costruttore che accetta quelle classi come parametri).

Questo non viene considerato nel nostro caso perché abbiamo un solo costruttore per Polar.

Viene restituito un "wrapper", che è il seguente:

public class CartesianToPolar implements Wrapper {
public Object[] wrap(Object[] object) {
return new Object[] {Math.sqrt((Math.pow((Double) object[0],2.0))+Math.pow((Double) object[1],2.0)),new Angle(Math.atan2((Double) object[1], (Double) object[0]))};
}
}


Il wrapper inverso, cioè da Polare a Cartesiano, che può essere reperito tramite l'annotazione associata al costruttore della implementazione polare, è il seguente:


public class PolarToCartesian implements Wrapper{
public Object[] wrap(Object[] object) {
return new Object[]{(Double)object[0]*
Math.cos(((Angle)object[1]).getValue()),
(Double)object[0]*Math.sin(((Angle)object[1]).getValue())};
}
}


La factory sfrutta queste informazioni:


public class ComplexNumbersFactory {
....
public static ComplexNumber getComplexFromCartesianPar(double first ,double second)
{
if (CARTESIAN.equals(implementation))
{
return new ComplexNumberCartesian(first,second);
}

if (POLAR.equals(implementation))
{

ComplexNumber converted = (ComplexNumber) Utilities.getInstanceOfThisActualGivenConstructorOfOther(
ComplexNumberPolar.class,ComplexNumberCartesian.class,
new Object[]{first,second},
new Class[]{double.class,double.class},
new Class[]{double.class,Angle.class});
return converted;
}
throw new RuntimeException("unadmitted implementation mode "+implementation);
}

}

Questo è il codice che reperisce il tutto reperisce il codice di conversione, esegue la conversione, e restituisce l'equivalente istanza nell'oggetto target:


public static Object getInstanceOfThisActualGivenConstructorOfOther(
Class targetClass,
Class originClass,
Object[] instanceOriginCompatible,
Class[] instanceOriginClasses,
Class[] instTargetClass)
{
try {
Constructor constructor = originClass.getConstructor(instanceOriginClasses);
WrapperToObject wrapper = (WrapperToObject) constructor.getAnnotation(instanceConverter.class).instanceConverterMap().newInstance();
Object[] convertedPars = ((wrapper.getWrapperForClassConstructorWithParameters(targetClass, instTargetClass,instanceOriginClasses).wrap(instanceOriginCompatible)));
Constructor targetConstructor = targetClass.getConstructor(instTargetClass);
Object convertedObject = targetConstructor.newInstance(convertedPars);
return convertedObject;

} catch (Exception e) {
throw new RuntimeException(e);
}
}


Se la factory è settata in modo cartesiano, restituisce l'implementazione cartesiana senza nessuna conversione.

Se la factory è settata in modo polare, allora essa utilizza una funzione di conversine interrogando la classe ComplexNumberCartesian. Cioè chiede al suo costruttore, di fornire un wrapper in grado di eseguire il mapping tra parametri di istanza per il tipo concreto Cartesian, a parametri di istanza per il tipo concreto Polar.


Eventuali nuove estensioni non implicano cambiamenti al codice che ne faccia uso (salvo che eventualmente dover settare una proprietà), ma solo nella factory, purché queste nuove implementazioni rispettino il vincolo di dichiarare come mapparsi nelle implementazioni preesistenti (e viceversa).

La conversione, dovrebbe anche rispettare il principio che la conversione B->A applicata alla conversione A->B dovrebbe essere l'identità.

Verifichiamo con junit la creazione di due diverse implementazioni dello stesso numero, e ne testiamo l'uguaglianza:

     ComplexNumber first = new ComplexNumberCartesian(1.0,1.0);
ComplexNumbersFactory.setImplementation(ComplexNumbersFactory.POLAR);
ComplexNumber second= ComplexNumbersFactory.getComplexFromCartesianPar(1.0,1.0);

assertEquals(((ComplexNumberPolar)second).getAngle(),new Angle(Math.PI/4));
assertEquals(((ComplexNumberPolar)second).getMagnitude(),Math.sqrt(2.0));

assertEquals(first,second);


Il tipo concreto restituito dopo che abbiamo settato la factory in modo polar è appunto polare, e quindi il cast non da eccezione, ed inoltre sfruttiamo dei metodi aggiuntivi che solo il polar mette a disposizione, che sono getAngle e getMagnitude che restituiscono i valori che ci aspettiamo coerenti per il numero 1+i.


Riassumendo

Rispetto a diversi scopi un tipo concreto piuttosto che un altro può avere vantaggi di efficienza in casi particolari, ma bisogna nascondere la rappresentazione concreta per evitare che il programma chiamante dipenda da queste nuove implementazioni.

Usiamo attributi per rafforzare il legame che c'è tra classi imparentate tra loro.

"Extends" o "implements" garantisco che sintatticamente possono essere applicati a metodi deifiniti in termini della loro classe astratta (o interfaccia) ma niente di più.

Il dover mettere anche questa metainformazione dichiarativa può significare: ehi... se stai creando una nuova implementazione dovresti anche occuparti di dichiarare come fare a rendere possibile sostituire la tua implementazione al posto di quelle preesistenti, garantendo che tutto funzioni allo stesso modo di prima.


(nota: il codice versionato è stato rifattorizzato rispetto a quanto scritto in questo post, quindi potrebbe non corrispondere in nomi di classi packages).


Saluti e Buon Anno!

T.

mercoledì 5 dicembre 2007

Critico musicale Richard Benson



Richard Benson è chiamato a dare un giudizio sui cantanti che non sono stati accettati al festival di Sanremo.

primo video
secondo video
terzo video

Informazioni personali

La mia foto
I have been coding from the old C64 times. Studied Computer Sciences at Milan University. I also worked there in technical operations. Many years of experiences in coding Java and C#, desktop and web applications, with practices like unit testing. I used to play with 3d graphics in architecture recently with Blender 3d. Now I look for support related to some projects I am working on, oriented in automation in tourism related services, using functional programming framework, specifically F# and Suave.IO. email
tonyx1 (at) gmail.com github https://github.com/tonyx