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"

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