Un programmatore nel corpo di un fumettaro.

mercoledì 3 marzo 2010

Programmazione Orientata agli Oggetti Lez. 2

Nella prima lezione abbiamo parlato di classi, oggetti e di come utilizzarli per raggiungere un risultato finale: fare una torta.

In questa lezione ci addentreremo nei dettagli.

Nella prima lezione abbiamo definito un "robot da cucina" come una classe molto semplice con alcuni metodi accessibili:

impostaVelocità(intero),
impostaUova(intero),
monta().

In questa lezione esamineremo quei metodi nascosti che, pur non visibili, servono al funzionamento della classe.

Il costruttore o inizializzatore.

Il costruttore di una classe è il primo metodo che viene eseguito nel momento in cui si istanzia un oggetto. Questo metodo non è visibile e non è richiamabile dall'interno di altri metodi.

Serve ad inizializzare l'oggetto, o meglio i suoi attributi.

Nel nostro esempio il robot potrebbe inizializzarsi nel momento in cui viene attaccata la spina alla presa di corrente ed eseguire il seguente metodo:

controllaTensione(): metodo nascosto non accessibile dall'esterno.

Immaginando che attaccare la spina significhi istanziare un oggetto il primo metodo che verrebbe invocato in automatico sarebbe quello di controllare il voltaggio della sorgente elettrica.
controllaTensione() è un metodo nascosto, privato, e può essere eseguito solo da altri membri della classe, come lui potrebbero essercene altri, per esempio "impostaPotenza(intero)" potrebbe essere usato da impostaVelocità(intero) per far si che le lame vadano più veloci.

A fronte delle nuove informazioni possiamo riscrivere la nostra classe usando dei risolutori di visibilità:

Classe RobotBase

pubblici:
 costruttore(),
 impostaVelocità(intero),
 impostaUova(intero),
 monta(),
privati:
 controllaTensione(),
 impostaPotenza().
 intero velocità,
 intero quantità,
 intero potenza.

A prima vista il costruttore dovrebbe andare tra i metodi privati, in realtà istanziare un oggetto significa eseguire il costruttore quindi se fosse messo tra i privati l'oggetto non potrebbe essere creato.

Raffinata la conoscenza delle classi è tempo di imparare a programmare in maniera orientata agli oggetti.

L'OOP è molto semplice se non si è mai programmato un computer e si inizia da zero, molto difficile se si proviene da esperienze di linguaggi procedurali. Personalmente reputo una assoluta idiozia la tendenza ad insegnare il C++ partendo dal C, sono linguaggi totalmente diversi che condividono solo la forma e le parole chiave, il "vantaggio" di poter programmare in maniera procedurale con il C++ è fonte inesauribile di errori e di brutte pezze sopra altri errori, tutti con un unico padre: l'errata ingegnerizzazione del software. Per programmare ad oggetti bisogna imparare a pensare ad oggetti, quindi organizzare il software in oggetti, chi proviene dai procedurali pensa solo al flusso di funzioni che portano al risultato immaginado un enorme contenitore "main.cpp" che richiama una serie di procedure "incluse". Pensare ad oggetti richiede molta più fatica perché il software va costruito in piccoli pezzi autonomi, classi, che genereranno oggetti che, come i lego, assemblati tra loro costituiranno il software finale. Riprendendo l'esempio della torta della prima lezione, chi proviene dal C e affini non pensa a creare un frustino, pensa solo alla funzione di montatura delle uova, non pensa al robot ma alla procedura che amalgama l'impasto, per lui è:

uovaMontate = montaUova(uova);
impasto = impasta(uovaMontate, zucchero, farina);
torta = cuoci(burro, impasto);

Tutto è più facile e veloce, non a caso dove serve velocità ed efficienza si usa il C al posto del C++, i driver delle periferche vengono scritti in C per esempio. La nota negativa è che si può riutilizzare poco o nulla, per fare la focaccia dobbiamo riscrivere tutto, mentre con gli oggetti ci basta derivare il RobotBase e aggiungere le variabili acqua e olio più un metodo per impastarli.


L'OOP e il tool di sviluppo Qt.

Il tool di sviluppo Qt di Nokia è un ottimo strumento per programmare ad oggetti, un semplice "hello world!" in una finestra fa capire tutto ciò che abbiamo detto fino ad ora.











file: hw_mainwindow.h

#ifndef HWMAINWINDOW_H

#define HWMAINWINDOW_H


#include <QtGui/QMainWindow>

class QLabel;


/*Definiamo la classe che mostrerà la finestra
ci basta derivare la finestra principale Qt: QMainWindow
*/

class HWMainWindow : public QMainWindow //si crea la classe derivata da QMainWindow
{
public:
  HWMainWindow(QWidget * parent = 0); //Il costruttore
private:
    QLabel *hw_label; //Lo mettiamo per mostrare la parte privata, potremmo ometterlo
};

#endif //HWMAINWINDOW_H


file: hw_mainwindow.cpp

#include "hw_mainwindow.h"
#include <QtGui/QLabel>

HWMainWindow HWMainWindow(QWidget *parent)
 : QMainWindow(parent)
{
    //Creiamo l'oggetto etichetta dalla classe Qt:QLabel
    hw_label = new QLabel(this);

    //Impostiamo il testo dell'etichetta
    hw_label->setText("hello world");

    //usiamo un metodo della QMainWindow per impostare l'etichetta come oggetto principale della finestra
    setCentralWidget(label);

}


file: main.cpp

#include "hw_mainwindow.h"
#include <QApplication>

main(int argc, char *argv[])
{
  // crea un oggetto QApplication con gli stessi valori immessi al lancio del programma.
  QApplication app(int argc, char *argv[]);

  // crea un oggetto HWMainWindow, la finestra derivata che abbiamo fatto prima;
  HWMainWindow hw_MainWindow;

  // esegue il metodo di QMainWindow per visulaizzare la finestra
  hw_MainWindow.show();

  //Entra nel loop di ascolto degli eventi, in questo caso aspetta che si spinga il pulsante di chiusura
  return app.exec();
}



Se esaminiamo bene tutto il codice noteremo che abbiamo usato pochi costrutti tipici del C++, abbiamo usato gli include, il main nella sua forma standard, abbiamo definito le classi e creato gli oggetti con "new".

Tutto il resto sono solo metodi appartenenti alle classi.
Tutto è elegante e leggibile, anche non conoscendo il C++ è grosso modo intuibile cosa fanno i vari pezzi.
Il main.cpp dice solo:
Crea l'applicazione,
crea la finestra,
mostra la finestra,
aspetta che si chiuda la finestra.

Se voglio mettere un pulsante sotto l'etichetta, mi basta toccare solo la mia classe derivata HWMainWindow.










file: hw_mainwindow.cpp

#include "hw_mainwindow.h"
#include <QtGui/QLabel>
#include <QtGui/QPushButton>
#include <QtGui/QVBoxLayout>

HWMainWindow HWMainWindow(QWidget *parent)
 : QMainWindow(parent)
{
    //Creiamo l'oggetto etichetta dalla classe Qt:QLabel
    hw_label = new QLabel(this);

    //Impostiamo il testo dell'etichetta
    hw_label->setText("hello world");

    //Creiamo l'oggetto pulsante
    QPushButton *button = new QPushButton(this);
 
    //Impostiamo il testo del pulsante
    button->setText("pulsante inutile");

    //Creiamo un widget contenitore completamente invisibile all'utente
    QWidget *mainWidget = new QWidget(this);

    //Nel momento in cui usiamo più elementi grafici dobbiamo distribuirli nella finestra
    //Questa classe li distribuisce in verticale tutti all'interno del widget contenitore mainWidget
    QVBoxLayout *vlayout = new QVBoxLayout(mainWidget);

    //Mettiamo prima l'etichetta
    vlayout->addWidget(label);

    //poi il pulsante
    vlayout->addWidget(button);

    //usiamo un metodo della QMainWindow per impostare il widget contenitore come
   //oggetto principale della finestra
    setCentralWidget(mainWidget);

}

La finestra è sempre la stessa ma ora contiene oggetti in più.
Non abbiamo toccato nulla se non la classe derivata.


Daniele Granata


Creative Commons License

Questa opera è pubblicata sotto una Licenza Creative Commons.

sabato 9 gennaio 2010

Programmazione Orientata agli Oggetti Lez. 1

Corso poco professionale su come affrontare l'ostico mondo dell'OOP (Object Oriented Programming).

Il nome OOP lascia poco alla fantasia: "Programmazione Orientata agli Oggetti", quindi è abbastanza scontato che tutto ruoti attorno agli "Oggetti".
Cosa è un "Oggetto"?

Un Oggetto è una istanza di una "Classe", una Classe è la definizione di un insieme di variabili e funzioni che in OOP prendono il nome di "attributi" e "metodi".

Dato che il corso è poco professionale prenderò ad esempio uno di quei robot da cucina che tagliano, sminuzzano, impastano ecc.
Un robot da cucina è un Oggetto. Un manuale che spieghi come costruirlo e usarlo sarebbe la sua Classe.
Il robot ha la funzione "triturare" (il metodo) da usare con una data "velocità " (l'attributo).




Nell'OOP ci si avvale di tre proprietà principali:
Incapsulamento,
Ereditarietà,
Polimorfismo.

Incapsulamento
L'incapsulamento è la proprietà per cui un Oggetto contiene al suo interno, incapsula, sia gli attributi che i metodi. Alcuni di questi metodi sono accessibili dall'esterno, altri sono utilizzati solo dall'oggetto stesso e quindi invisibili, inoltre un oggetto ben progettato non rende accessibile i suoi attributi in maniera diretta ma solo attraverso dei metodi appositi.
Il nostro robot ha tanti metodi invisibili, noi non sappiamo cosa faccia girare la lama, come funzioni il blocco sicurezza ecc. sappiamo solo "triturare" perché è il metodo reso disponibile all'esterno. L'attributo "velocità" non è accessibile direttamente ma solo tramite la manopola apposita, quindi un altro metodo visibile che ci permette di modificare l'attributo "velocità". Altri attributi saranno nascosti come la "tensione elettrica", la "frizione", ecc.

Ereditarietà.
L'ereditarietà permette alle Classi di poter essere "derivate", cioè trasformate affinché vengano aggiunti nuovi metodi e/o attributi. I metodi aggiunti possono essere totalmente nuovi o sostituirne di esistenti (polimorfismo).
Il produttore del nostro robot potrebbe produrne uno identico al nostro ma con una lama aggiuntiva per "affettare", quindi avrebbe un nuovo metodo "affettare". La Classe del nostro robot è detta Classe Base, la nuova si chiama Classe Derivata.

Polimorfismo.
Con il polimorfismo una Classe Derivata può sostituire uno o più metodi della Classe Base con equivalenti con diversa implementazione.
Il nuovo robot derivato oltre ad affettare potrebbe avere una lama per "triturare" più affilata che trita più finemente, si avrebbe quindi lo stresso metodo "triturare" ma con un risultato diverso.

Uso di classi e oggetti.
Ora che abbiamo una bella infarinatura di Classi e Oggetti possiamo entrare nel dettaglio del loro uso.
Immaginiamo di dover preparare una torta!

Per fare la torta useremo alcune classi, le deriveremo e le metteremo in relazione tra loro.
Esaminiamo la ricetta:

Ingredienti:
4 uova
400gr farina
300gr zucchero
50gr burro

Preparazione:
Montare a neve 4 albumi di uovo.
Impastare gli albumi a neve con la farina e lo zucchero.
Imburrare una teglia.
Versare l'impasto nella teglia.
Infornare e cuocere per 45 minuti a 150gradi.

Dalla ricetta si evince che avremo bisogno di un frustino elettrico per montare gli albumi.
Un robot per impastare zucchero, farina e uova montate.
Una teglia da imburrare in cui verseremo l'impasto.
Un forno dove inserire la teglia e cuocere la torta.

Nota: con "intero" intendiamo un numero intero.

Classe FrustinoBase: è un frustino utile per montare le uova.
Ha come metodi accessibili:
impostaVelocità(intero),
impostaUova(intero),
monta() restituisce le uova montate.
Ha come attributi:
intero velocità,
intero quantità
Gli attributi non sono accessibili se non con i metodi "impostaXXX".

Classe RobotBase:  è il nostro robot di partenza che sa solo triturare.
Ha come metodi accessibili:
impostaVelocità(intero),
impostaQuantità(intero),
tritura().
Ha come attributi:
intero velocità,
intero quantità
Gli attributi non sono accessibili se non con i metodi "impostaXXX".

Dobbiamo impastare la farina con lo zucchero e le uova montate con il frustino. Per questa attività il robot non va bene e ha bisogno di essere potenziato, così lo deriviamo.

Classe RobotImpastatore derivato da RobotBase: è un robot da cucina che impasta.
Aggiungiamo i metodi pubblici:
impostaFarina(intero)
impostaZucchero(intero)
impostaUova(UovaMontate)
impasta() restituisce un Impasto.
Aggiungiamo nuovi attributi:
intero farina;
intero zucchero;
UovaMontate uovaMontate;

Classe TegliaBase: è una teglia.
Ha come metodi accessibili:
impostaImpasto(Impasto),
impostaBurro(intero),
imburra().
Ha come attributi:
intero burro,
Impasto impasto
Gli attributi non sono accessibili se non con i metodi "impostaXXX".

Classe FornoBase: è un forno.
Ha come metodi accessibili:
impostaTemperatura(intero),
impostaMinuti(intero),
inforna(TegliaBase),
cuociTorta() restituisce una torta.
Ha come attributi:
intero temperatura,
intero minuti
Gli attributi non sono accessibili se non con i metodi "impostaXXX".

Useremo anche due classi contenitore:
Classe UovaMontate e Classe Impasto sono classi che non è necessario approfondire perché ininfluenti, le dichiariamo solo per mantenere l'esempio concreto, UovaMontate conterrebbe le uova, Impasto conterrebbe UovaMontate, farina e zucchero.

Ora che abbiamo dichiarato i nostri utensili andiamo ad usarli.

Preparazione della torta

Iniziamo con il montare il bianco dell'uovo a neve:
Creiamo l'Oggetto FrustinoBase che monta solo le uova:

FrustinoBase mioFrustino.
Ora ho il mio oggetto frustino e posso usarlo per montare le uova.

Poiché gli oggetti incapsulano metodi e attributi ci vorrà un sistema per identificarli come appartenenti all'oggetto specificato, useremo il nome dell'oggetto seguito da un punto ".".

Metto quattro albumi nella ciotola del frustino:
mioFrustino.impostaUova(4);
Imposto la velocità a 3:
mioFrustino.impostaVelocità(3);
Monto le uova:
UovaMontate uovaMontate = mioFrustino.monta();

Abbiamo montato le uova.
Ora impastiamo:
Creiamo il robot impastatore:
RobotImpastatore mioImpastatore;
Aggiungiamo gli ingredienti:
mioImpastatore.impostaFarina(400);
mioImpastatore.impostaZucchero(300);
mioImpastatore.impostaUova(uovaMontate);

Ora selezioniamo la velocità e impastiamo:
mioImpastatore.impostaVelocità(2);
Impasto impasto = mioImpastatore.impasta();


Prendiamo la teglia e imburriamola:
TegliaBase miaTeglia;
miaTeglia.impostaBurro(50);
miaTeglia.imburra();

Versiamo l'impasto nella teglia:
miaTeglia.impostaImpasto(impasto);

Ora prepariamo il forno:
FornoBase mioForno;
mioForno.inforna(miaTeglia);
mioForno.impostaTemperatura(160);
mioForno.impostaMinuti(45);

Cuociamo:
Torta torta = mioForno.cuociTorta();

La torta è pronta.
Riepiloghiamo il flusso delle operazioni:

FrustinoBase mioFrustino;
mioFrustino.impostaUova(4);
mioFrustino.impostaVelocità(3);
UovaMontate uovaMontate = mioFrustino.monta();

RobotImpastatore mioImpastatore;
mioImpastatore.impostaFarina(400);
mioImpastatore.impostaZucchero(300);
mioImpastatore.impostaUova(uovaMontate);
mioImpastatore.impostaVelocità(2);
Impasto impasto = mioImpastatore.impasta();

TegliaBase miaTeglia;
miaTeglia.impostaBurro(50);
miaTeglia.imburra();
miaTeglia.impostaImpasto(impasto);

FornoBase mioForno;
mioForno.inforna(miaTeglia);
mioForno.impostaTemperatura(160);
mioForno.impostaMinuti(45);
Torta torta = mioForno.cuociTorta();


Avrete notato che alcuni oggetti ne usano altri, tipo il forno che usa la teglia, questo perché una Classe crea un nuovo tipo di dato e gli oggetti di questa Classe saranno dei dati, ricordiamo che un oggetto incapsula metodi ed attributi, quindi dati.

Per ora ci fermiamo, abbiamo imparato le basi e siamo già riusciti a fare una torta, incredibile.


Creative Commons License
Questa opera è pubblicata sotto una Licenza Creative Commons.