marți, 17 noiembrie 2015

Lectia 9 Supraîncărcarea constructorilor şi definirea constructorilor în clasele derivate



Metodele sunt funcțiii membru ale unei clase. Pot fi statice (metode de clasă) sau nestatice (metode de instanță). 
Pot exista mai multe metode diferite cu acelasi nume intr-o clasa, cu conditia sa difere lista de parametri prin numar sau tip.
Legarea uneia sau alteia dintre aceste funcții omonime se face la momentul compilării (legare timpurie),  pe baza listei de parametri: se va lega funcția a cărei semnătură se potrivește cel mai bine cu lista de parametri actuali. Acest mecanism se numește polimorfism ad-hoc.
Clasele derivate pot redefini metode ale clasei de baza, pastrand numarul si tipul parametrilor, cu conditia aplicarii modificatorilor virtual (aplicat metodei din superclasa) si override (aplicat  metodei din  clasa derivata). Mecanismul este valabil si  pentru constructori. În cazul acesta, funcția nu se  leagă de către compilatorul C#  ci de către sistemul .NET (Windows) la momentul execuției (legare întârziată, la run-time), pe criteriul identificării tipului obiectului al cărui membru este metoda apelată. Acesta este  polimorfism de moștenire.

Clasa  Copil are doi constructori: unul fara parametri si unul cu 1 parametru string.
Clasa derivata Fetita  redefineste ambii constructori.
Clasa derivata Baiat nu defineste nici un consructor, ca urmare se apeleaza constructorul implicit al clasei fara parametri.

namespace Lectia9
{
    public class Copil
    {
        protected string nume; //data accesibila numai in interiorul
                               //clasei si a claselor derivate
        public const int nr_max = 10; //constanta
        public static int nr_copii = 0; //camp simplu (variabila)
        static Copil[] copii = new Copil[nr_max]; //camp de tip
        //tablou de referinte  tip Copil alocat static, maxim 10 elemente
        public static void adaug_copil(Copil c) //metodă  cu parametru referinta Copil
        {
            copii[nr_copii++] = c;
            if (nr_copii == nr_max)
                throw new Exception("Prea multi copii");
        }
        public static void afisare() //metodă
        {
            Console.WriteLine("Sunt {0} copii:", nr_copii);
            for (int i = 0; i < nr_copii; i++)
                Console.WriteLine("Nr.{0}. {1}", i + 1, copii[i].nume);
        }
        public Copil() //constructorul fara parametri ai clasei
        {
            Console.Write("Nume {0} ", nr_copii+1);
            nume = Console.ReadLine();
        }
        public Copil(string s) //constructor cu parametru
        {
            nume = s;
        }
    }
    class Baiat : Copil { }
    class Fetita : Copil
    {
        public Fetita(string s) : base(s)
  //base semnifica faptul ca   se face mai intai apel la constructorul
  //din clasa de baza, apoi se inlocuieste numele cu „Fetita „+nume
        }  
nume = "Fetita " + nume;
        }
        public Fetita():base()
        { }
    }
    class Program
    {
        public static void Main()
    {
            Fetita f = new Fetita();
            Copil.adaug_copil(f);
//apel la metoda statica (metoda de clasa)
//referinţa noului obiect se memorează în tabloul static copii
//(caracteristic clasei) şi se incrementează data statică nr_copii
            Baiat b = new Baiat();
            Copil.adaug_copil(b);
            Copil c = new Copil();
            Copil.adaug_copil(c);
            Copil.afisare(); //se afişează o listă cu numele celor 3 copii
            Console.ReadKey();
       }
    }

}


Mostenirea si suprascrierea  metodelor nestatice ale  clasei Copil in clasele derivate
In programul de mai sus se adauga in clasa de baza metodele se_joaca() cu modificatorul virtual si se_joaca(sting s)  fara virtual. In clasa derivata Fetita se suprascrie metoda se_joaca() cea virtuala.
public class Copil
{
protected string nume;
// …  constructorii
public virtual void se_joaca( ) //virtual –> asadar  functia se poate
{ //suprascrie in clasele derivate, dar nu obligatoriu. In absenta lui virtual nu s-ar putea suprascrie.
Console.WriteLine("{0} se joaca.", this.nume);
}
// urmează un polimorfism ad-hoc: definirea unei funcții omonime cu altă semnătură
public void se_joaca(string jucaria) //supradefinirea metodei
{ //se_joaca
Console.WriteLine("{0} se joaca cu {1}.",this.nume,jucaria);
}
...
}
class Fetita: Copil
{
public override void se_joaca( ) //suprascriere = override a unei metode virtuale din clasa de baza Copil. Apelul acestei metode se va rezolva la run-time - polimorfism de moștenire
{
Console.WriteLine("{0} plimba pisica.", this.nume);
}
}
...

In metoda Main() se adauga apelul catre functiile se_joaca, atat cea suprascrisa (fara parametru) cat si cea nesuprascrisa  (cu parametru), plecand de la un obiect de tip Fetita si de la un obiect de tip Baiat. Functia apelata este diferita, de la caz la caz.

Fetita f = new Fetita( );
f.se_joaca("pisica");  // Andreea se joaca cu pisica – apel metoda cu parametru din clasa de baza Copil. Legare timpurie.  Este un apel nepolimorfic către o metodă moștenită.
f.se_joaca( );              // Andreea plimba pisica – apel metoda suprscrisa din clasa derivata Fetita. Legare întârziată. Apel către o funcție moștenită supraîncărcată  = polimorfică.
Baiat b = new Baiat ( );
b.se_joaca("calculatorul"); //Andrei se joaca cu calculatorul – apel metoda cu parametru din clasa de baza
b.se_joaca( );       // Andrei se joaca – apel metoda fara parametru din clasa de baza. Toate apelurile către metode ale clasei de bază sunt legate timpuriu. Moștenire fără polimorfism.

Evidențierea legării întârziate a metodelor supraîncărcate ale  clasei derivate.

Adaugam in metoda Main() urmatoarele instructiuni:

Copil d = f; 

// referinta d de tip Copil refera un obiect de tip Fetita. 

d.se_joaca();

// Apel catre functia supraincarcata se_joaca() a subclasei Fetita. Legarea apelului se face la run-time catre //functia clasei Fetita, deoarece abia atunci se identifica tipul obiectului referit de d in urma atribuirii d=f.

Rezultatul este ca se va afisa la consola textul Andreea plimba pisica.






duminică, 8 noiembrie 2015

C#8 Principiile programării orientate pe obiecte

Concepte teoretice

Un tip de date abstract  (TDA)este o entitate caracterizată printr-o structură de date şi un ansamblu de operaţii aplicabile acestor date.

Exemplu:  tipul abstract USER al  unui site de comerț electronic. Care sunt datele semnificative?  Astfel, „culoarea ochilor” este irelevantă în acest caz, în timp ce „data naşterii” poate fi importantă. Care sunt operațiile relevante?  operaţii specifice ca „se înregistrează”, „comandă on-line” pot fi relevante, în timp ce operaţia „mănâncă” nu este, în cazul nostru. Evident, nici nu se pun în discuţie date sau operaţii nespecifice („numărul de laturi” sau acţiunea „zboară”).

Operaţiile care sunt accesibile din afara TDA formează interfaţa acesteia. Astfel,
operaţii interne cum ar fi conversia datei de naştere la un număr standard calculat de la
01.01.1900 nu fac parte din interfaţa tipului de date abstract, în timp ce operaţia „plasează o
comandă on-line” face parte, deoarece permite interacţiunea cu alte obiecte (SITE, STOC etc.).

Numim instanţă a unui tip de date abstract o „concretizare” a tipului respectiv,
formată din valori efective ale datelor.

Un tip de date obiectual este un tip de date care implementează un tip de date abstract.

obiect1
- date1
- met1

obiect4
- date4
- met4

obiect2
-date2
- met2

obiect3
- date3
- met3

Vom numi metode operaţiile implementate în cadrul tipului de date abstract.
Numim membri ai unui tip de date obiectual datele şi metodele definite mai sus.

Folosirea unui tip de date obiectual tip presupune:
- existenţa definiţiei acestuia
- apelul metodelor
- accesul la date.

 Crearea unei instanţe noi a unui tip obiectual, presupune operaţii specifice de „construire” a noului obiect, metoda corespunzătoare purtând numele de constructor.

 La desfiinţarea unei instanţe şi eliberarea spaţiului de memorie aferent datelor sale, se aplică o metodă specifică numită destructor.

 Principalul tip obiectual întâlnit în majoritatea mediilor de dezvoltare (Visual Basic, Delphi, C++, Java, C#) poartă numele de clasă (class). Există şi alte tipuri obiectuale (struct, object). O instanţă a unui tip obiectual poartă numele de obiect.

 clasele pot avea la implementare:
- date şi metode caracteristice fiecărui obiect din clasă (membri de tip instanţă),
- date şi metode specifice clasei (membri de tip clasă).

 Prin supraîncărcare se înţelege posibilitatea de a defini în acelaşi domeniu de
vizibilitate mai multe funcţii cu acelaşi nume, dar cu parametri diferiţi ca tip şi/sau ca număr.
Ansamblul format din numele funcţiei şi lista sa de parametri reprezintă o modalitate unică de identificare numită semnătură sau amprentă

 Moştenire

Pentru tipurile de date obiectuale class este posibilă o operaţie de extindere sau specializare a comportamentului unei clase existente prin definirea unei clase noi ce moşteneşte datele şi metodele clasei de bază, cu această ocazie putând fi redefiniţi unii membri existenţi sau adăugaţi unii membri noi. Operaţia mai poartă numele de derivare. Clasa din care se moşteneşte se mai numeşte clasă de bază sau superclasă. Clasa care moşteneşte se numeşte subclasă, clasă derivată sau clasă descendentă.

 Ca şi în Java, în C# o subclasă poate moşteni de la o singură superclasă, adică avem de-a face cu moştenire simplă; aceeaşi superclasă însă poate fi derivată în mai multe subclase distincte. O subclasă, la rândul ei, poate fi superclasă pentru o altă clasă derivată. O clasă de bază împreună cu toate clasele descendente (direct sau indirect) formează o ierarhie de clase.

 În contextul mecanismelor de moştenire trebuie amintiţi modificatorii abstract şi sealed
aplicaţi unei clase, modificatori ce obligă la şi respectiv se opun procesului de derivare. Astfel, o clasă abstractă trebuie obligatoriu derivată, deoarece direct din ea nu se pot obţine obiecte prin operaţia de instanţiere, în timp ce o clasă sigilată (sealed) nu mai poate fi derivată (e un fel de terminal în ierarhia claselor).

 O metodă abstractă este o metodă pentru care nu este definită o implementare, aceasta urmând a fi realizată în clasele derivate din clasa curentă care trebuie să fie şi ea abstractă (virtuală pură, conform terminologiei din C++).
O metodă sigilată este o metodă care nu mai poate fi redefinită în clasele derivate din clasa curentă.

 Polimorfism. Metode virtuale

Un obiect polimorfic este cel capabil să ia diferite forme, să se afle în diferite stări, să aibă comportamente diferite. Polimorfismul obiectual, care trebuie să fie abstract, se manifestă în lucrul cu obiecte din clase aparţinând unei ierarhii de clase, unde, prin redefinirea unor date sau metode, se obţin membri diferiţi având însă acelaşi nume. Acest lucru este posibil doar în cazul limbajelor ce permit „legarea întârziată”. La limbajele cu „legare timpurie”, adresa la care se face un apel al unui
subprogram se stabileşte la compilare. La limbajele cu legare întârziată, această adresă se stabileşte doar in momentul rulării, putându-se calcula distinct, în funcţie de contextul în care apare
apelul.

Exemplu: dacă este definită clasa numită PIESA (de şah), cu metoda nestatică muta (pozitie_initiala, pozitie_finala), atunci subclasele TURN şi PION trebuie să aibă metoda muta definită în mod diferit. Mecanismele POO permit stabilirea, în momentul apelului, a clasei proxime căreia îi aparţine obiectul T şi apelarea metodei corespunzătore (mutare de pion sau tură sau altă piesă).
Pentru a permite acest mecanism, metodele care necesită o decizie contextuală (în momentul apelului), se declară ca metode virtuale (cu modificatorul virtual). În mod curent, în C# modificatorului virtual al funcţiei din clasa de bază, îi corespunde un specificator override al
funcţiei din clasa derivată ce redefineşte funcţia din clasa de bază.

Principiile POO sunt:

1. abstractizarea - principiu care permite identificarea caracteristicilor şi comportamentului obiectelor ce ţin nemijlocit de domeniul problemei. Rezultatul este un model. În urma abstractizării, entităţile din domeniul problemei se definesc prin clase.

2. încapsularea – numită şi ascunderea de informaţii, este caracterizată prin 2 aspecte:
a. Gruparea comportamentelor şi caracteristicilor într-un tip abstract de date
b. Definirea nivelului de acces la datele unui obiect

3. moştenirea – organizează şi facilitează polimorfismul şi încapsularea permiţând definirea si crearea unor clase specializate plecând de la clase (generale) care sunt deja definite - acestea pot împărtăşi (şi extinde) comportamentul lor fără a fi nevoie de redefinirea aceluiaşi comportament.

4. Polimorfismul - posibilitatea mai multor obiecte dintr-o ierarhie de clase de a utiliza denumiri de metode cu acelaşi nume dar, cu un comportament diferit.

 Exemplu de ierarhie de clase:

public class Copil { }
public class Fetita: Copil { }
public sealed class Baiat: Copil { }

Constructori

 Constructorul este o funcţie care face parte din corpul unei clase. Corpul constructorului este format din instrucţiuni care se execută la crearea unui nou obiect al clasei respective (sau la crearea clasei,  în cazul constructorilor cu modificatorul static).
- pot exista mai mulţi constructori care se pot diferenţia prin lista lor de parametri
- constructorii nu pot fi moşteniţi
- dacă o clasă nu are definit niciun constructor, se va asigna automat constructorul fără parametri al clasei de bază (clasa object, dacă nu este precizată clasa de bază)

 public class Copil
{
  protected string nume; //data accesibila numai in interiorul
  //clasei si a claselor derivate
  public Copil ( ) //constructorul fara parametri ai clasei
   {
     nume = Console.ReadLine( );
   }
}

class Fetita: Copil
{ }
...
Fetita f = new Fetita ( );
Copil c = new Copil ( );

Supraîncărcarea constructorilor şi definirea constructorilor în clasele derivate

 public class Copil
{
   protected string nume; //data accesibila numai in interiorul
   //clasei si a claselor derivate
   public Copil ( ) //constructorul fara parametrii ai clasei
   {nume = Console.ReadLine( );}
   public Copil (string s) //constructor cu parametru
   {nume = s;}
}

class Fetita: Copil
{
   public Fetita (string s): base(s) 
                    //base semnifica faptul ca se face apel la constructorul din clasa de baza
     {  nume = "Fetita "+ nume;
     }
 }
...
Copil c1 = new Copil ( ); //numele copilului se citeste de la tastatura
Copil c2 = new Copil ("Gigel"); //numele lui c2 va fi Gigel
Fetita f1 = new Fetita ( );
Fetita f2 = new Fetita ("Maria");