Skillbook Logo
foto profilo

Skill Factory

Lista post > TypeScript - Lezione 5: Classi ed Oggetti

TypeScript - Lezione 5: Classi ed Oggetti

Gino Visciano | Skill Factory - 10/07/2020 17:52:46 | in Tutorials

TypeScript pur essendo un linguaggio usato per sviluppare applicazioni lato Front-end, è un linguaggio Object Oriented, quindi è importante conoscere i concetti di Classe ed Oggetto.

Una Classe definisce un tipo di oggetto, in pratica è il codice sorgente dell'oggetto che verrà creato in meomria. L'operazione che trasforma il codice  sorgente della classe in oggetto si chiama istanza

L'esempio seguente mostra come si può istanziare in memoria un oggetto di nome persona usando la classe Persona:

Persona persona=new Persona();

1) Persona  è la classe, rappresenta il tipo di oggetto;
2) persona variabile che contiene l'indirizzo (riferimento)  dell'oggetto istanziato con il comando new;
3) Persona() costruttore, metodo usato per inizializzare l'oggetto creato.  

Istanziare significa creare in memoria con il comando new un oggetto, usando come modello la classe. L'indirzzo dove viene allocato l'oggetto, viene assegnato alla variabile dell'istanza, come ad esempio  persona

In fase di progettazione il linguaggio UML (Unified Modeling Language) è molto utile per disegnare un tipo di classe che poi potra essere scritta con qualunque linguaggio di programmazione object oriented (orientato agli oggetti).

L'esempio seguente mostra il diagramma di classe della classe Persona:

Le Classi  possono contenere i seguenti elementi:

attributi
metodi
costruttori

Per convenzione i nomi delle Classi iniziano sempre con la lettera maiuscola e se sono composti da più parole, anche le altre parole devono iniziare con la lettera maiuscola.

ATTRIBUTI
Gli attributi sono variabili della classe, per questo motivo sono anche chiamate  variabili d'istana, sono le proprietà che si usano per memorizzare i dati che appartengono all'oggetto.

Gli attributi  se sono pubblici (+ simbolo UML) possono anche essere usati dai metodi di altre classi, altrimenti se sono privati (- simbolo UML  possono essere usati solo dai metodi della stessa classe.

Se un attributo è protected (# simbolo UML) , in caso di ereditarietà è visibile anche ai metodi della classe figlia.


METODI
metodi sono funzioni contenute nelle classi, le azioni dei metodi determinano il comportamento degli oggetti.

Nei linguaggi di programmazione ad oggetti, la logica applicativa (istruzioni), può essere implementata solo all'interno dei metodi.

 

 

metodi se sono pubblici   (+ simbolo UML)  possono essere eseguiti anche dai metodi di altre classi, se sono privati  (- simbolo UML) possono essere eseguiti solo dai metodi della stessa classe.

Se un metodo è protected (# simbolo UML) , in caso di ereditarietà è visibile anche ai metodi della classe figlia.

Per convenzione i  metodi che hanno il nome che inizia con i prefissi get e set si usano per assegnare o leggere i valori degli attributi privati dell'oggetto a cui appartengono. 

Ad esempio,  dato che gli  attributi della classe  Persona sono privati, per gestirli dovete usare i seguenti metodi pubblici:

setNome
getNome
setCognome
getCognome
setEta
getEta 

COSTRUTTORI
I costruttori sono metodi speciali usati  per istanziare gli oggetti e per inizializzare gli attributi.

Persona persona=new Persona();

Persona() => costruttore.

Per creare un costruttore inTypeScript  dovete implementare un metodo che si chiama constructor senza indicare il tipo restituito

In una classe deve esistere sempre almeno un costruttore senza argomenti, chiamato costruttore di default, come mostra l'esempio seguente:

public constructor(){}

Se necessario il costruttore di default può anche essere usato per inizializzare gli attributi dell'oggetto creato, come mostra l'esempio seguente:
 
public constructor(){
            this.id=1;
            this.nome="Marco";
            this.cognome="Rossi";
            this.dataDiNascita="05/10/1990";
            this.luogoDiNascita="Milano";
            this.sesso="Maschio";
            this.codiceFiscale="mrcrss90r05l268f";
}

Un costruttore con argomenti si chiama costruttore parametrizzato, a differenza di quello di default, permette di passare i valori per inizializzare l'oggetto durante l'istanza, come mostra l'esempio  seguente:

Persona persona=new Persona(1,'Paolo','Rossi','10/02/1991','Milano','Maschio','pllrss91a02l234f');

L'esempio seguente mostra come si crea un costruttore parametrizzato:

public constructor(id:number,nome:string,cognome:string,dataDinascita:Date,luogoDiNascita:string,sesso:string,codiceFiscale:string){
            this.id=id;
            this.nome=nome;
            this.cognome=cognome;
            this.dataDiNascita=dataDinascita;
            this.luogoDiNascita=luogoDiNascita;
            this.sesso=sesso;
            this.codiceFiscale=codiceFiscale;

Esempio 1

L'esempio seguente mostra come creare una classe Persona con gli attributi privati ed i metodi set e get pubblici.
La parola chiave export indica che la classe può essere importata anche in un altro modulo con il comando import.


export class Persona{
    private id:number;
    private nome:string;
    private cognome:string;
    private dataDiNascita:Date;
    private luogoDiNascita:string;
    private sesso:string;
    private codiceFiscale:string;
    public setId(id:number):void{
        this.id=id;
    }
    public getId():number{
        return this.id;
    }
    public setNome(nome:string):void{
        this.nome=nome;
    }
    public getNome():string{
        return this.nome;
    }
    public setCognome(cognome:string):void{
        this.cognome=cognome;
    }
    public getCognome(){
        return this.cognome;
    }
    public setDataDiNascita(dataDiNascita:Date):void{
        this.dataDiNascita=dataDiNascita;
    }
    public getDataDiNascita():Date{
        return this.dataDiNascita;
    }
    public setLuogDiNascita(luogoDiNascita:string):void{
        this.luogoDiNascita=luogoDiNascita;
    }
    public getluogoDiNascita():string{
        return this.luogoDiNascita;
    }
    public setSesso(sesso:string):void{
        this.sesso=sesso;
    }
    public getSesso():string{
        return this.sesso;
    }
    public getCodiceFiscale():string{
        return this.codiceFiscale;
    }
    public setCodiceFiscale(codiceFiscale:string):void{
        this.sesso=codiceFiscale;
    }
    public toString():string{
        let optionsIntl.DateTimeFormatOptions = {
            day: "numeric"month: "numeric"year: "numeric"
        };
 
           return this.id+","+this.nome+","+this.cognome+","+
                  this.dataDiNascita.toLocaleDateString("en-GB"options)
                  +","+this.luogoDiNascita+","+this.sesso+","+this.codiceFiscale;
    }
    public equals(obj:Object):boolean{
        if (this.toString()!=obj.toString()){
            return false;
        }
        return true;
    }
        // Sovraccarico del costruttore (Overload)
        public constructor();
        public constructor(id:number,nome:string,cognome:string,dataDinascita:Date,
                           luogoDiNascita:string,
                           sesso:string,codiceFiscale:string);
        public constructor(id?:number,nome?:string,cognome?:string,dataDinascita?:Date,
                           luogoDiNascita?:string,sesso?:string,codiceFiscale?:string){
            if(arguments.length>0){
                this.id=id;
                this.nome=nome;
                this.cognome=cognome;
                this.dataDiNascita=dataDinascita;
                this.luogoDiNascita=luogoDiNascita;
                this.sesso=sesso;
                this.codiceFiscale=codiceFiscale;
            }
 
        }    
}
 

 

SOVRACCARICO DEL COSTRUTTORE  IN TYPESCRIPT (OVERLOAD)

L' overload è la proprietà dei linguaggi di programmazione ad oggetti che permettere l'implementazione nella stessa classe di metodi con lo stesso nome, ma firma diversa. 

Si definisce firma il nome di un metodo più i tipi degli argomenti passati in input. 

Ad esempio, le firme dei costruttori della classe Persona, sono le seguenti:

1) constructor
2) constructor, number, string, string,  Date, string, string, string

In TypeScript l'overlad si può applicare usando una funzione con argomenti opzionali che in base al numero di argomenti passati in input assume un comportamento diverso.
Per conoscere quali e quanti argomenti vengono passati ad una funzione con argomenti opzionali potete usare l'interfaccia arguments.
Per definire le firme consentite per eseguire la funzione con argomenti opzionali, dovete indicare le  interfacce corrispondenti, come mostra l'esempio seguente:

// Interfaccia che definisce la firma del costruttore di default
public constructor();
// Interfaccia che definisce la firma del costruttore parametrizzato
public constructor(id:number,nome:string,cognome:string,dataDinascita:Date,luogoDiNascita:string,sesso:string,codiceFiscale:string); 
// Funzione con argomenti opzionali, il punto indicativo "?" rende il parametro facoltativo
public constructor(id?:number,nome?:string,cognome?:string,dataDinascita?:Date,luogoDiNascita?:string,sesso?:string,codiceFiscale?:string){
            if(arguments.length>0){
                this.id=id;
                this.nome=nome;
                this.cognome=cognome;
                this.dataDiNascita=dataDinascita;
                this.luogoDiNascita=luogoDiNascita;
                this.sesso=sesso;
                this.codiceFiscale=codiceFiscale;
            }
}

Esempio 2

L'esempio seguente utilizza la classe Persona, creata nell'esempio precedente, per gestire un array di persone.

L'inserimento dei dati avviene attraverso il Form seguente che permette di aggiungere una persona all'array, con il pulsante "Salva" e visualizzare l'elenco di tutte le persone inserite con il pulsante "Elenco".


 

Per ottenere il Form dovete utilizzare il codice HTML seguente:

 
<!--gui_persona.html-->
<html>
<head>
    <script lang="JavaScript" src="persone.js"></script>
</head>
<body>
<table>
<form>
<tr><th>PERSONA</th><th></th></tr>
<tr><td>Nome:</td><td><input type="text" id="nome" value=""></td></tr>
<tr><td>Cognome:</td><td><input type="text" id="cognome" value=""></td></tr>
<tr><td>Data di nascita:</td><td><input type="text" id="data" value=""></td></tr>
<tr><td>Luogo di nascita:</td><td><input type="text" id="luogo" value=""></td></tr>
<tr><td>Sesso:</td><td><input type="text" id="sesso" value=""></td></tr>
<tr><td>Codice Fiscale:</td><td><input type="text" id="cf" value=""></td></tr>
<tr><td></td><td><input type="button" onclick="salva();" value="Salva"> 
<input type="button" onclick="addElenco();" value="Elenco"> <input type="reset" 
       value="reset"></td></tr>
</form>
</table>
--- Elenco Persone ---
<div id="elenco"></div>
</body>
</html>
 


Il tag <script lang="JavaScript" src="persone.js"></script> permette d'importare il modulo JavaScript, risultato della compilazione con tsc del codice TypeScript seguente:


// persone.ts
import {Personafrom "./persona";
 
var persone:Persona[]=[];  //var persone:Persona[]=[]
var id:number=0;
 
// Funzione associata al metodo onclick del pulsante Salva
function salva():void{
    var nome:string=(<HTMLInputElement>document.getElementById("nome")).value;
    var cognome:string=(<HTMLInputElement>document.getElementById("cognome")).value;
    var strData:string=(<HTMLInputElement>document.getElementById("data")).value;
    var luogo:string=(<HTMLInputElement>document.getElementById("luogo")).value;
    var sesso:string=(<HTMLInputElement>document.getElementById("sesso")).value;
    var cf:string=(<HTMLInputElement>document.getElementById("cf")).value;
    var data:Date=new Date(strData);
    id++;
    persone.push(new Persona(id,nome,cognome,data,luogo,sesso,cf));
 }
 
 
// Funzione associata al metodo onclick del pulsante Elenco
 function addElenco() { 
    // crea un nuovo elemento div
    // gli assegna un contenuto
    var currentDiv = document.getElementById("elenco");  
    persone.forEach((persona:Persona)=>{
     // aggiungi il nodo di testo al div appena creato
    var newDiv = document.createElement("div"); 
     newDiv.style.color="blue";
     newDiv.style.fontSize="20";
     var newContent = document.createTextNode(persona.toString()); 
     newDiv.appendChild(newContent);  
     document.body.insertBefore (newDivcurrentDiv); 
     })
   // aggiungi l'elemento appena creato e il suo contenuto nel DOM
 }
 


L'immagine seguente mostra il Form con l'elenco delle persone inserite: 

INTERFACCE

In TypeScript un'interfaccia può essere usata sia per definire un tipo di dato multivalore che serve ad esempio per creare un oggetto JSON, sia per definire  l'elenco dei metodi astratti da implementare in una classe. In questa lezione ci occuperemo delle interfacce che contengono metodi astratti.

Per comprendere come utilizzare un'interfaccia che contiene metodi astratti, facciamo l'esempio di una classe CRUD che usa come repository array di tipo Persona.
Come è nota la classe per svolgere il proprio compito deve garantire le seguenti operazioni:

1) Inserimento (Create);
2) Lettura (Read), di una o tutte le persone memorizzare nel repository;
3) Modifica (Update);
4) Cancellazione (Delete). 

L'esempio seguente mostra come utilizza un'interfaccia come modello per implementare in una classe che deve svolgere le operazioni previste dal CRUD.

Esempio 3

 
//personacrud.ts
import {Personafrom './persona'
 
interface IPersonaCRUD{
    inserisci(persona:Persona):boolean;
    modifica(indice:number,persona:Persona):boolean;
    cancella(indice:number):boolean;
    leggi(indice:number):Persona;
    leggi():Persona[];
}
 
export class PersonaCRUD implements IPersonaCRUD{
    private persone:Persona[];
    inserisci(personaPersona): boolean {
       this.persone.push(persona);
       return true;
    }
    modifica(indice:numberpersonaPersona): boolean {
        this.persone[indice]=persona;
        return true;
    }
    cancella(indicenumber): boolean {
        this.persone.splice(indice,1);
        return true;  
    }
    // Overload (polimorfismo dei metodi)
    leggi(indicenumber): Persona;
    leggi(): Persona[];
    leggi(indice?: number): Persona | Persona[] {
        if (arguments.length==1){
            return this.persone[indice];
        } else{
        return this.persone;
        }
    }
 
}
 

 

CLASSI ASTRATTE

Nella programmazione ad oggetti una classe astratta è un pattern, perché serve per implementare una soluzione applicativa.
Le classi astratte permettono d'implementare metodi che usano il risultato di uno o più metodi astratti, di cui non ne conosciamo ancora il comportamento.
Naturalmente le classi astratte non possono istanziare oggetti, perché contengono sia metodi implementati, sia metodi astratti e quindi devono necessariamente essere ereditate.
La classe figlia verrà utilizzata per implementare il comportamento dei metodi astratti della classe padre

Esempio 4  

//geometria_astratta.ts
//Classe astratta che può essere solo ereditata
abstract class FiguraGeometrica{
    // Attributo di tipo privato, visibile solo nell'oggetto, globalmente.
    private tipoFigura:string;
    public getInfoFigura():string[]{
        var infoFigura:string[]=[];
        infoFigura.push("Tipo figura:"+this.tipoFigura);
        // Uso il risultato dei metodi astratti
        // che verranno implementati nella classe figlia 
        infoFigura.push(this.getCaratteristiche());
        infoFigura.push("Perimetro  :"+this.getPerimetro());
        infoFigura.push("Area       :"+this.getArea());
        return infoFigura;
    }
    getTipoFigura():string{
        return this.tipoFigura;
    }
    // metodi astratti
    abstract getCaratteristiche():string;
    abstract getPerimetro():number;
    abstract getArea():number;
    // Costruttore alimentato dalla classe figlia
    // viene utilizzato per indicare il tipo di figura geometrica
    public constructor(tipoFigura:string){
        this.tipoFigura=tipoFigura;
    }
}
 
class Cerchio extends FiguraGeometrica{
    // Attraverso il costruttore assegno all'attributo raggio il suo valore
    public constructor(private raggio:number){
        // Il metodo super esegue il costruttore del padre
        // e gli passa il valore "Cerchio"
        super("Cerchio");
    }
    // Implemento i metodi astratti ereditati
    public getCaratteristiche():string{
        return "Raggio     :"+this.raggio;
    }
    public getPerimetro(): number {
        return 2*Math.PI*this.raggio;
    }
    public getArea(): number {
        return Math.pow(this.raggio,2)*Math.PI;
    }
}
 
class Quadrato extends FiguraGeometrica{
    // Attraverso il costruttore assegno all'attributo latoA il suo valore   
    public constructor(private latoA:number){
        // Il metodo super esegue il costruttore del padre
        // e gli passa il valore "Cerchio"
        super("Quadrato");
    }
    // Implemento i metodi astratti ereditati
    public getCaratteristiche():string{
        return "Lato       :"+this.latoA;
    }
    public getPerimetro(): number {
        return 4*this.latoA;
    }
    public getArea(): number {
        return Math.pow(this.latoA,2);
    }
}
 
class Rettangolo extends FiguraGeometrica{
    // Attraverso il costruttore assegno agli attributi 
    // latoA e latoB i loro valori   
    public constructor(private latoA:number,private latoB:number){
        // Il metodo super esegue il costruttore del padre
        // e gli passa il valore "Cerchio"
        super("Rettangolo");
    }
    // Implemento i metodi astratti ereditati
    public getCaratteristiche():string{
        return "Lati       :"+this.latoA+", "+this.latoB;
    }
    public getPerimetro(): number {
        return (2*this.latoA)+(2*this.latoB);
    }
    public getArea(): number {
        return this.latoA*this.latoB;
    }
}
 
class Triangolo extends FiguraGeometrica{
    // Attraverso il costruttore assegno agli attributi 
    // latoA, latoB e latoC i loro valori   
    public constructor(private latoA:number,private latoB:number,private latoC:number){
        // Il metodo super esegue il costruttore del padre
        // e gli passa il valore "Cerchio"
        super("Triangolo");
    }
    // Implemento i metodi astratti ereditati
    public getCaratteristiche():string{
        return "Lati       :"+this.latoA+", "+this.latoB+", "+this.latoC;
    }
    public getPerimetro(): number {
        return this.latoA+this.latoB+this.latoC;
    }
    public getArea(): number {
        let sp=this.getPerimetro()/2;
        let area=Math.sqrt(sp*(sp-this.latoA)*(sp-this.latoB)*(sp-this.latoC))
        return area;
    }
}
class Geometria {
    /* Dichiaro un array di tipo FiguraGeometrica che 
    può contenere cerchi,quadrati, rettangoli e triangoli (Upcasting).  
    Questo è possibile solo perché le classi cerchio,quadrato,
    rettangolo e triangolo sono diventate simili, perché hanno ereditato tutte
    la classe FiguraGeometrica (Polimorfismo degli oggetti)*/
    private figureGeometriche:FiguraGeometrica[]=[]
    // Il metodo add grazie all'Upcasting può ricevere anche 
    // riferimenti di oggetti di tipo cerchio,quadrato, rettangolo e triangolo.
    public add(figuraGeometrica:FiguraGeometrica):void{
        this.figureGeometriche.push(figuraGeometrica);
    }
    public stampa(){
        this.figureGeometriche.forEach(figuraGeometrica =>
            figuraGeometrica.getInfoFigura().forEach(info => console.log(info)))
    }
}
class Main{
public static main():void{
    var cerchio:FiguraGeometrica=new Cerchio(10);
    var quadrato:FiguraGeometrica=new Quadrato(20);
    var rettangolo:FiguraGeometrica=new Rettangolo(10,20);
    var triangolo:FiguraGeometrica=new Triangolo(5,10,10);
    var geometria:Geometria=new Geometria();
    geometria.add(cerchio);
    geometria.add(quadrato);
    geometria.add(rettangolo);
    geometria.add(triangolo);
    geometria.stampa();
}    
}    
//Main
Main.main();
 
-----------------------------------------------------------------------------------
Tipo figura:Cerchio
Raggio     :10
Perimetro  :62.83185307179586
Area       :314.1592653589793
Tipo figura:Quadrato
Lato       :20
Perimetro  :80
Area       :400
Tipo figura:Rettangolo
Lati       :10, 20
Perimetro  :60
Area       :200
Tipo figura:Triangolo
Lati       :5, 10, 10
Perimetro  :25
Area       :24.206145913796355
 

 

CLASSI SINGLETON

Nella programmazione ad oggetti una classe singleton è un pattern, perché serve per implementare una soluzione applicativa.
Le classi singleton permettono d'istanziare una sola ricorrenza di un oggetto per evitare duplicati.
Di solito le classi singleton si usano per creare i repository, array che memorizzano oggetti. 

Esempio 5  

 
//singleton.ts
interface IRepository{
    inserisci(obj:Object):boolean;
    modifica(indice:number,obj:Object):boolean;
    cancella(indice:number):boolean;
    leggi(indice:number):Object;
    leggi():Object[];
}
 
export class Repository implements IRepository{
    // Poiché questa classe non può essere istanziata
    // la variabile che deve contenere il riferimento del singolo
    // oggetto istanziato deve istanziarsi da sola con l'operatore static
    private static instance:Repository=null;
    private repository:Object[]=[];
    // Poiché questa classe non può essere istanziata, il metodo 
    // getRepository() che istanzia l'unica ricorrenza dell'oggetto 
    // di tipo Repository deve istanziarsi da solo con l'operatore static
    public static getRepository():Repository{
        // Se instance è null viene istanziato l'oggetto di tipo Repository
        // altrimento l'oggetto già esiste e viene restituito il suo riferimento 
        if(this.instance==null){
            //Il costruttore può essere eseguito anche se privato
            //perché getRepository() è un metodo della classe
            this.instance=new Repository(); 
        }
        return this.instance;
    }
    inserisci(objObject): boolean {
       this.repository.push(obj);
       return true;
    }
    modifica(indice:numberobjObject): boolean {
        this.repository[indice]=obj;
        return true;
    }
    cancella(indicenumber): boolean {
        this.repository.splice(indice,1);
        return true;  
    }
    // Overload (polimorfismo dei metodi)
    leggi(indicenumber): Object;
    leggi(): Object[];
    leggi(indice?: number): Object | Object[] {
        if (arguments.length==1){
            return this.repository[indice];
        } else{
        return this.repository;
        }
    }
    // Il costruttore di una classe singleton è privato
    // per non permettere d'istanziare oggetti
    private constructor(){}
}
 
// Model utente
class Utente{
    public constructor(private id:number,private nome:string,
                       private cognome:string,eta:number,private sesso:boolean){}
}
 
// Classe Controller
class Main{
    public static main(){
        var utentiUno=Repository.getRepository();
        var utentiDue=Repository.getRepository();
        utentiUno.inserisci(new Utente(1,"Mara","Rossi",30,true));
        utentiUno.inserisci(new Utente(2,"Roberta","Verdi",25,true));
        utentiUno.inserisci(new Utente(3,"Carlo","Bianchi",50,false));
        utentiDue.inserisci(new Utente(4,"Ugo","Rossini",20,false));
        utentiDue.inserisci(new Utente(5,"Raffaele","Palomba",25,false));
        utentiDue.inserisci(new Utente(6,"Corrado","Belvedere",50,false));
        console.log("---------- Prima istanza di Repository ----------");
        utentiUno.leggi().forEach(utente => console.log(utente));
        console.log("---------- Seconda istanza di Repository ----------");
        utentiDue.leggi().forEach(utente => console.log(utente));
    }
}
 
// main
Main.main();
 
------------------------------------------------------------------------------------
---------- Prima istanza di Repository ----------
Utente { id: 1, nome: 'Mara', cognome: 'Rossi', sesso: true }
Utente { id: 2, nome: 'Roberta', cognome: 'Verdi', sesso: true }
Utente { id: 3, nome: 'Carlo', cognome: 'Bianchi', sesso: false }
Utente { id: 4, nome: 'Ugo', cognome: 'Rossini', sesso: false }
Utente { id: 5, nome: 'Raffaele', cognome: 'Palomba', sesso: false }
Utente { id: 6, nome: 'Corrado', cognome: 'Belvedere', sesso: false }
---------- Seconda istanza di Repository ----------
Utente { id: 1, nome: 'Mara', cognome: 'Rossi', sesso: true }
Utente { id: 2, nome: 'Roberta', cognome: 'Verdi', sesso: true }
Utente { id: 3, nome: 'Carlo', cognome: 'Bianchi', sesso: false }
Utente { id: 4, nome: 'Ugo', cognome: 'Rossini', sesso: false }
Utente { id: 5, nome: 'Raffaele', cognome: 'Palomba', sesso: false }
Utente { id: 6, nome: 'Corrado', cognome: 'Belvedere', sesso: false }
 

 

Nella prossima lezione approfondiamo il paradigma Object Oriented.


<< Lezione precedente           Lezione successiva >> | Vai alla prima lezione


T U T O R I A L S    S U G G E R I T I


EDUCATIONAL GAMING BOOK (EGB) "H2O"

Nell'era dello SMART LEARNING e di PYTHON i libri non si scrivono, ma si sviluppano, in questo modo chi studia, può sperimentare ed apprendere contemporaneamente; un libro con queste caratteristiche lo possiamo definire un  Educational Gaming Book (EGB).

"H2Oè un EGB che descrive tutte le caratteristiche dell'acqua, la sostanza formata da molecole di H2O, che attraverso il suo ciclo di vita garantisce la sopravvivenza di tutti gli esseri viventi del Pianeta

L'obiettivo dell'EGB è quello di far conoscere ai giovani le proprietà dell'acqua, sotto molti aspetti uniche, per sensibilizzarli a salvaguardare un bene comune raro, indispensabile per la vita


Per il DOWNLOAD di "H2Oclicca qui.

Share Button
TOP