* * * *
13 Visitatori, 0 Utenti

28 Nov 2011 - OOP e Classi

Con le classi si introduce anche il discorso di programmazione orientata agli oggetti (OOP), in quanto il Free Pascal (quindi di rimando anche Lazarus) è considerato tale.
Per capire cosa si intende per programmazione orientata agli oggetti ci viene in aiuto Wikipedia che ne da una definizione ottima:

La programmazione orientata agli oggetti (OOP, Object Oriented Programming) è un paradigma di programmazione, che prevede di raggruppare in un'unica entità (la classe) sia le strutture dati che le procedure che operano su di esse, creando per l'appunto un "oggetto" software dotato di proprietà (dati) e metodi (procedure) che operano sui dati dell'oggetto stesso. La programmazione orientata agli oggetti può essere vista come una modulazione di oggetti software sulla base degli oggetti del mondo reale.
La modularizzazione di un programma viene realizzata progettando e realizzando il codice sotto forma di classi che interagiscono tra di loro. Un programma ideale, realizzato applicando i criteri dell'OOP, sarebbe completamente costituito da oggetti software (istanze di classi) che interagiscono gli uni con gli altri.
La programmazione orientata agli oggetti è particolarmente adatta a realizzare interfacce grafiche.

Sulla base di ciò Wikipedia definisce le classi in questo modo:

Nella programmazione orientata agli oggetti una classe è un costrutto di un linguaggio di programmazione usato come modello per creare oggetti. Il modello comprende attributi e metodi che saranno condivisi da tutti gli oggetti creati.
Una classe può rappresentare una persona, un luogo oppure una cosa, ed è quindi l'astrazione di un concetto implementata in un programma per computer. Fondamentalmente essa definisce al proprio interno lo stato ed il comportamento dell'entità di cui è rappresentazione. I dati che descrivono lo stato sono memorizzati nelle variabili membro, mentre il comportamento è descritto da blocchi di codice riutilizzabile chiamati metodi.

Vediamo ora i vantaggi della programmazione ad oggetti
Incapsulamento
L'incapsulamento è la proprietà per cui i dati che definiscono lo stato interno di un oggetto sono accessibili ai metodi dell'oggetto stesso, mentre non sono visibili ai clients. Per alterare lo stato interno dell'oggetto, è necessario invocarne i metodi, ed è questo lo scopo principale dell'incapsulamento. Infatti, se gestito opportunamente, esso permette di vedere l'oggetto come una black-box, cioè una "scatola nera" di cui, attraverso l'interfaccia, è noto cosa fa, ma non come lo fa.

Ereditarietà
Il meccanismo dell'ereditarietà permette di derivare nuove classi a partire da quelle già definite. Una classe derivata attraverso l'ereditarietà, o sottoclasse, mantiene i metodi e gli attributi delle classi da cui deriva (classi base, o superclassi); inoltre, può definire i propri metodi o attributi, e può ridefinire il codice eseguibile di alcuni dei metodi ereditati tramite un meccanismo chiamato overriding.
Quando una classe eredita da una sola superclasse si parla di eredità singola; viceversa, si parla di eredità multipla.
L'ereditarietà può essere usata come meccanismo per ottenere l'estensibilità e il riuso del codice, e risulta particolarmente vantaggiosa quando viene usata per definire sottotipi, sfruttando le relazioni is-a esistenti nella realtà di cui la struttura delle classi è una modellizzazione. Oltre all'evidente riuso del codice della superclasse, l'ereditarietà permette la definizione di codice generico attraverso il meccanismo del polimorfismo.

Polimorfismo
Nella programmazione ad oggetti, con il nome di polimorfismo per inclusione, si indica il fatto che lo stesso codice eseguibile può essere utilizzato con istanze di classi diverse, aventi una superclasse comune.

Vediamo un po' di codice per capire meglio, come dichiarare una classe semplice ed usarla:
Codice: [Seleziona]

type
ClasseUtenti=class
private
NomeUtente: string;
public
costructor Create();
destructor Free();
                procedure setUtente(valore: string);
                function getUtente(): string;
end;
Constructor ClasseUtenti.Create();
begin
end;
Destructor ClasseUtenti.Free();
begin
end;
procedure ClasseUtenti.setUtente(valore: string);
begin
     NomeUtente:=valore;
end;
function ClasseUtenti.getUtente(): string;
begin
     getUtente:=NomeUtente;
end;


Con queste righe di codice abbiamo appena creato la nostra prima classe che contiene il nome di un ipotetico utente. Come si può notare la variabile che conterrà il nome dell'utente si chiama NomeUtente ed è una stringa. Questa variabile (proprietà) è stata dichiarata all'interno della sezione private, ovvero non è visibile all'esterno della classe, e può essere letta o settata solo attraverso funzioni e procedure che fanno parte della classe stessa. La funzione per leggere e la procedura (metodi) per settare  il nome dell'utente sono dichiarate nella sezione public, ovvero sono visibili all'esterno della classe. Un altra cosa che va notata è che all'interno del type...end; sono presenti solo le dichiarazioni dei metodi stessi, i metodi sono scritti per intero fuori dal type...end; e per identificare che appartengono ad una classe si mette il nome della classe prima del nome del metodo separati solo da un punto. Una nota particolare va posta sulle due diciture constructor e distructor che servono per far capire quale funzione crea (allocca in memoria) la classe e quale la distrugge (deallocca dalla memoria).

Usare la classe appena creata è davvero facile, diamo un occhiata a come fare, creiamo una nuova applicazione console con questo codice:
Codice: [Seleziona]

program project1;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, SysUtils, CustApp,
  { you can add units after this };
{INIZIO MIO CODICE}
type
ClasseUtenti=class
private
NomeUtente: string;
public
constructor Create();
destructor Free();
                procedure setUtente(valore: string);
                function getUtente(): string;
end;
{FINE MIO CODICE}
type

  { TMyApplication }

  TMyApplication = class(TCustomApplication)
  protected
    procedure DoRun; override;
  public
    constructor Create(TheOwner: TComponent); override;
    destructor Destroy; override;
    procedure WriteHelp; virtual;
  end;

{ TMyApplication }

procedure TMyApplication.DoRun;
var
  ErrorMsg: String;
  app:ClasseUtenti; {VARIABILE DICHIARATA DA ME}
begin
  // quick check parameters
  ErrorMsg:=CheckOptions('h','help');
  if ErrorMsg'' then begin
    ShowException(Exception.Create(ErrorMsg));
    Terminate;
    Exit;
  end;

  // parse parameters
  if HasOption('h','help') then begin
    WriteHelp;
    Terminate;
    Exit;
  end;
  { add your program here }

  {
   dopo aver dichiarato una variabile di nome app di tipo ClasseUtenti
   ora devo far si che a questa variabile venga assegnato uno spazio in
   memoria per poterci scrivere dentro e quindi eseguo la riga successiva
   dove indico che ad app voglio assegnare NomeClasse.Costruttore
  }
  app:=ClasseUtenti.Create();
  app.setUtente('xinyiman');  {setto la variabile con il nome del mio utente, nel mio caso xinyiman}
  writeln('Utente: ',app.getUtente()); {ora vado a stampare a video il nome dell'utente inserito nella classe poco fa}
  app.Free(); {ho fatto tutto quello che dovevo con questa variabile app e quindi libero dalla memoria lo spazio che occupa richiamando il distruttore}
  // stop program loop
  Terminate;
end;

constructor TMyApplication.Create(TheOwner: TComponent);
begin
  inherited Create(TheOwner);
  StopOnException:=True;
end;

destructor TMyApplication.Destroy;
begin
  inherited Destroy;
end;

procedure TMyApplication.WriteHelp;
begin
  { add your help code here }
  writeln('Usage: ',ExeName,' -h');
end;


{INIZIO MIO CODICE}
Constructor ClasseUtenti.Create();
begin
end;
Destructor ClasseUtenti.Free();
begin
end;
procedure ClasseUtenti.setUtente(valore: string);
begin
     NomeUtente:=valore;
end;
function ClasseUtenti.getUtente(): string;
begin
     getUtente:=NomeUtente;
end;
{FINE MIO CODICE}

var
  Application: TMyApplication;
begin
  Application:=TMyApplication.Create(nil);
  Application.Title:='My Application';
  Application.Run;
  Application.Free;
end.


Ed ecco un esempio che spiega come usare l'ereditarietà in Lazarus, ampliando l'esempio precedente.
Codice: [Seleziona]

program project1;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, SysUtils, CustApp
  { you can add units after this };
{INIZIO MIO CODICE}
type
ClasseUtenti=class
private
NomeUtente: string;
public
     constructor Create(); virtual;
     destructor Free();
              procedure setUtente(valore: string);
              function getUtente(): string;
end;

type
        AdvClasseUtenti=class(ClasseUtenti)
        public
              constructor Create(); override; //il termine override significa "calpestare" o "passare sopra" e serve per dire che questo costruttore deve andare a sostituire il costruttore della superclasse
              function GetUtenteInverso(): string;
end;
{FINE MIO CODICE}
type

  { TMyApplication }

  TMyApplication = class(TCustomApplication)
  protected
    procedure DoRun; override;
  public
    constructor Create(TheOwner: TComponent); override;
    destructor Destroy; override;
    procedure WriteHelp; virtual;
  end;

{ TMyApplication }

procedure TMyApplication.DoRun;
var
  ErrorMsg: String;
  app:AdvClasseUtenti; {VARIABILE DICHIARATA DA ME}
begin
  // quick check parameters
  ErrorMsg:=CheckOptions('h','help');
  if ErrorMsg'' then begin
    ShowException(Exception.Create(ErrorMsg));
    Terminate;
    Exit;
  end;

  // parse parameters
  if HasOption('h','help') then begin
    WriteHelp;
    Terminate;
    Exit;
  end;
  { add your program here }

  {
   dopo aver dichiarato una variabile di nome app di tipo ClasseUtenti
   ora devo far si che a questa variabile venga assegnato uno spazio in
   memoria per poterci scrivere dentro e quindi eseguo la riga successiva
   dove indico che ad app voglio assegnare NomeClasse.Costruttore
  }
  app:=AdvClasseUtenti.Create();
  writeln('Utente: ',app.getUtente()); {ora vado a stampare a video il fatto che non è stato inserito ancora nessun utente}
  app.setUtente('xinyiman');  {setto la variabile con il nome del mio utente, nel mio caso xinyiman}
  writeln('Utente: ',app.getUtente()); {ora vado a stampare a video il nome dell'utente inserito nella classe poco fa}
  writeln('Utente: ',app.GetUtenteInverso()); {ora stampo al contrario il nome dell'utente, funzione che ho aggiunto alla classe figlia}
  app.Free(); {ho fatto tutto quello che dovevo con questa variabile app e quindi libero dalla memoria lo spazio che occupa richiamando il distruttore}
  // stop program loop
  Terminate;
end;

constructor TMyApplication.Create(TheOwner: TComponent);
begin
  inherited Create(TheOwner);
  StopOnException:=True;
end;

destructor TMyApplication.Destroy;
begin
  inherited Destroy;
end;

procedure TMyApplication.WriteHelp;
begin
  { add your help code here }
  writeln('Usage: ',ExeName,' -h');
end;


{INIZIO MIO CODICE}
Constructor ClasseUtenti.Create();
begin
     Self.setUtente('');
end;
Destructor ClasseUtenti.Free();
begin
end;
procedure ClasseUtenti.setUtente(valore: string);
begin
     NomeUtente:=valore;
end;
function ClasseUtenti.getUtente(): string;
begin
     getUtente:=NomeUtente;
end;

constructor AdvClasseUtenti.Create();
begin
     inherited Create(); //inherited significa ereditato e vuol dire che vado ad eseguire prima il costruttore della super classe e poi il codice di questo costruttore
     {ovviamente potevamo escludere la riga sopra che inizia con inherited solo che non avrebbe eseguito il costruttore della superclasse, cosa non necessaria nel nostro esempio, ma fondamentale da sapere per livelli di programmazione un po' più avanzati di questo esempio}
     Self.setUtente('NESSUN UTENTE INSERITO'); //la procedura SetUtente l'ho ereditata dalla superclasse
end;

function AdvClasseUtenti.GetUtenteInverso(): string;
var
   i: integer;
   ret,appoggio: string;
begin
     ret:='';
     appoggio:=Self.getUtente();
     i:=Length(appoggio);
     while (i>=0) do
     begin
          ret:=ret + appoggio[i];
          Dec(i); //decremento la variabile i
     end;
     GetUtenteInverso:=ret;
end;
{FINE MIO CODICE}

var
  Application: TMyApplication;
begin
  Application:=TMyApplication.Create(nil);
  Application.Title:='My Application';
  Application.Run;
  Application.Free;
end.


Vediamo ora alcune parole riservate:
private - questo significa che gli elementi qui definiti, sono disponibili o visibili da altre classi procedure o funzioni definite all'interno della stessa unit di programma.
protected - questo significa che le voci definite qui sono solo disponibili o visibili da classi che discendono da quella classe antenata,ed eredita le sue proprietà o metodi
public - questo significa che le voci definite qui sono solo disponibili per qualunque unit che includa la unit corrente in essa con la clausula Uses
published - è simile alla sezione public, ma il compilatore genera anche il tipo di informazioni che è necessario per lo streaming automatico di queste classi. Spesso la lista delle voci appare nel Object Inspector di Lazarus; se non compare una lista published,tutti i campi public appaiono normalmente in the Object Inspector.

Metodi
Methods
Un metodo è esattamente come una procedura standard o una funzione, ma può avere qualche directives.
Alcuni dei metodi di definizione di cui sopra sono etichettati con la direttiva virtual; altri sono etichettati con la direttiva override.

virtual significa che il tipo o il grado effettivo di un metodo non è noto al momento della compilazione,ma è stato selezionato in run-time a seconda di ciò che effettivamente richiama il sotto-programma sul momento. Potrebbe essere considerato un segnaposto nella definizione della classe.
override significa che in fase di esecuzione la definizione data a livello locale può prendere il posto di una definizione ereditata da una classe antenata, soprattutto se fosse virtuale. Se si desidera utilizzare il metodo definito nella classe antenata, a volte è necessario chiamare in modo specifico con la clausola inherited come visto nell'esempio precedente.

Tutti i metodi virtual o override sono metodi dinamici, tutti gli altri vengono chiamati metodi statici.

Share on Twitter! Digg this story! Del.icio.us Share on Facebook! Technorati Reddit StumbleUpon

Articles in « PROGRAMMATORE ALZATI E CAMMINA CON LAZARUS »

Comments *

Commenting option has been turned off for this article.

Recenti

How To

Trascinare un file nel programma da xinyiman
DB concetti fondamentali e ZeosLib da xinyiman
Recuperare codice HTML da pagina web da xinyiman
Mandare mail con Lazarus da xinyiman
Stabilire il sistema operativo da xinyiman
Esempio lista in pascal da xinyiman
File INI da xinyiman
Codice di attivazione da xinyiman
Realizzare programmi multilingua da xinyiman
Lavorare con le directory da xinyiman
Utilizzare Unità esterne da Loryea
TTreeView da xinyiman
TTreeview e Menu da xinyiman
Generare controlli RUN-TIME da xinyiman
LazReport, PDF ed immagini da xinyiman
Intercettare tasti premuti da xinyiman
Ampliare Lazarus da xinyiman
Lazarus e la crittografia da xinyiman
System Tray con Lazarus da xinyiman
UIB: Unified Interbase da Microges2000
Il file: questo sconosciuto da Microges2000
Conferma di chiusura di un applicazione da xinyiman
Liste e puntatori da Microges2000
Overload di funzioni da Microges2000
Funzioni a parametri variabili da Microges2000
Proprietà da Microges2000
Conversione numerica da Microges2000
TImage su Form e Panel da Maverich
Indy gestiore server FTP lato Client da Maverich
PopUpMenu sotto Pulsante (TSpeedButton) da Maverich
Direttiva $macro da Microges2000
Toolbar da xinyiman
Evidenziare voci TreeView da Maverich
Visualizzare un file Html esterno da Maverich
StatusBar - aggirare l'errore variabile duplicata da Maverich
Da DataSource a Excel da xinyiman
Le permutazioni da xinyiman
Brute force da xinyiman
Indy 10 - Invio email con allegati da Maverich
La gestione degli errori in Lazarus da xinyiman
Pascal Script da xinyiman
Linux + Zeos + Firebird da xinyiman
Dataset virtuale da xinyiman
Overload di operatori da Microges2000
Lavorare con file in formato JSON con Lazarus da nomorelogic
Zeos ... dietro le quinte (prima parte) da Stilgar
Disporre le finestre in un blocco unico (come Delphi) da Maverich
Aspetto retrò (Cmd Line) da xinyiman
Come interfacciare periferica twain da Narciso
Ubuntu - aggiornare free pascal e lazarus da xinyiman
fpcup: installazioni parallele di lazarus e fpc da nomorelogic
Free Pascal e Lazarus sul Raspberry Pi da nomorelogic
Cifratura: breve guida all'uso dell'algoritmo BlowFish con lazarus e free pascal. da nomorelogic
Creare un server multithread da xinyiman
guida all'installazione di fpc trunk da subversion in linux gentoo da nomorelogic
Indice da nomorelogic
DB concetti fondamentali e connessioni standard da xinyiman
Advanced Record Syntax da nomorelogic
DB concetti fondamentali e DBGrid da xinyiman
DB concetti fondamentali e TDBEdit, TDBMemo e TDBText da xinyiman
Advanced Record Syntax: un esempio pratico da nomorelogic
Superclasse form base per programmi gestionali (e non) da nomorelogic
Superclasse form base per programmi gestionali (e non) #2 - log, exception call stack, application toolbox da nomorelogic
Superclasse form base per programmi gestionali (e non) #3 - traduzione delle form da nomorelogic
Superclasse form base per programmi gestionali (e non) #4 - wait animation da nomorelogic
Un dialog per la connessione al database:TfmSimpleDbConnectionDialog da nomorelogic
Installare lazarus su mac osx sierra da xinyiman
Utenti
  • Utenti in totale: 551
  • Latest: dax
Stats
  • Post in totale: 10670
  • Topic in totale: 1354
  • Online Today: 24
  • Online Ever: 74
  • (Luglio 09, 2012, 11:05:53 am)
Utenti Online
Users: 0
Guests: 13
Total: 13

Disclaimer:

Questo blog non rappresenta una testata giornalistica poiché viene aggiornato senza alcuna periodicità. Non può pertanto considerarsi un prodotto editoriale ai sensi della legge n. 62/2001.