Risipa de key_press | Programare

Programare .Net | Tehnici de programare | Tutoriale | Lectii si exemple

Risipa de key_press | Programare - Programare .Net | Tehnici de programare | Tutoriale | Lectii si exemple

Interfete vs. clase abstracte

Cred ca una din cele mai frecvente intrebari din lumea .Net este legata de diferentele dintre o interfata si o clasa abstracta.
In acest articol mi-am propus sa raspund la aceasta intrebare si sa exemplific, prin cod, diferentele dintre cele doua notiuni.

Ce este o interfata ?

O interfata defineste un set de metode, proprietati, evenimente, indexatori. Acesti membri vor fi implementati de o clasa sau chiar de o structura.
Pentru mai multe informatii despre interfete puteti consulta articolul Interfete in .Net.

Ce este o clasa abstracta ?

O clasa abstracta este o clasa care nu poate fi instantiata si este folosita pentru mostenire.
O clasa care nu este abstracta si care deriveaza dintr-o clasa abstracta, trebuie sa includa implementarile tuturor membrilor abstracti.

Vom crea o interfata si o clasa abstracta, apoi le vom implementa/mosteni.

Scriem codul pentru o interfata.

    /// <summary>
    /// interface for Person
    /// </summary>
    public interface IPerson
    {

        #region properties for IPerson interface

        string FirstName
        {
            get; set;
        }

        string LastName
        {
            get; set;
        }

        int Age
        {
            get; set;
        }

        #endregion

        #region methods for IPerson interface

        String Add();
        String Delete();
        int CalculateWage();

        #endregion
    }

Observatie 1:

Interfata contine doar signatura membrilor.

Observatie 2:

Daca nu se specifica nici un modificator de acces pentru interfata, atunci cel implicit va fi internal. Daca se afla in interiorul unei clase, o interfata poate avea modificatorii de access private, protected, public, internal.

Observatie 3:

O clasa poate implementa mai multe interfete.

Scriem codul pentru clasa abstracta:

    /// <summary>
    /// abstract class for Person
    /// </summary>
    public abstract class Person
    {
        #region fields for the abstrac class
        
        protected string firstName;
        protected string lastName;
        protected int age;
        
        #endregion

        #region properties for the abstract class
        public abstract string FirstName
        {
            get; set;
        }

        public abstract string LastName
        {
            get; set;
        }

        public abstract int Age
        {
            get; set;
        }
        #endregion

        #region completed methods for the abstract class

        string Add ()
        {
            return "New person " + firstName + " " + lastName + " added.";
        }

        string Delete ()
        {
            return "Person" + firstName + " " + lastName + " deleted.";    

        }

        #endregion

        #region abstract method

        public abstract int CalculateWage();  
        
        #endregion
    }

Observatie 1 :

Metoda CalculateWage este abstracta si poate fi implementata diferit in clasele derivate.

Observatie 2 :

Clasa abstracta poate avea ca membri si campuri (field).

Observatie 3 :

O clasa poate mosteni o singura clasa abstracta.

Observatie 4 :
O clasa poate furniza atat membri abstracti cat si membri cu implementare concreta.

Cream clasa Employee care va implementa interfata IPerson:

   /// <summary>
    /// class Employee implements IPerson interface
    /// </summary>
    class Employee : IPerson
    {
        //must define the filds
        //and implement the properties
        protected string firstName;
        protected string lastName;
        protected int age;

        public string FirstName
        {
            get { return firstName; }
            set { firstName = value; }
        }

        public string LastName
        {
            get { return lastName; }
            set { lastName = value; }
        }

        public int Age
        {
            get { return age; }
            set { age = value; }
        }

        //implement methods
        public string Add()
        {
            return "New Person " + firstName + " " + lastName + " added";
        }

        public string Delete()
        {
            return "Person " + firstName + " " + lastName + " deleted";
        }

        public int CalculateWage()
        {
            return 100;
        }
    }

Observatie :

Daca adaugam un nou membru la o interfata, atunci va trebui sa urmarim toate implementarile interfetei si sa scriem cod pentru implementarea noului membru.

Cream clasa Student care va mosteni clasa abstracta Person.

    class Student : Person
    {

        #region Overrides of Person

        public override string FirstName
        {
            get { return firstName; }
            set { firstName = value; }
        }

        public override string LastName
        {
            get { return lastName; }
            set { lastName = value; }
        }

        public override int Age
        {
            get { return age; }
            set { age = value; }
        }

        public override int CalculateWage()
        {
            return 0;
        }

        #endregion
    }

Observatie :

Daca adaugam un nou membru la o clasa abstracta ii putem adauga o implementare implicita si nu vom avea probleme cu codul existent.

Cream instante pentru clasele Student si Employee si le testam.

            Student s = new Student();
            s.FirstName = "marius";
            s.LastName = "istudor";
            s.Age = 24;

            Console.WriteLine(s.Add());
            Console.WriteLine(s.Delete());
            Console.WriteLine("Wage: {0}",s.CalculateWage());

            Employee e = new Employee();
            e.FirstName = "adrian";
            e.LastName = "zelter";
            e.Age = 34;

            Console.WriteLine(e.Add());
            Console.WriteLine(e.Delete());
            Console.WriteLine("Wage: {0}",e.CalculateWage());

Daca o clasa abstracta implementeaza o interfata, atunci vor trebui implementati toti membri interfetei.
Declaram o metoda virtual pentru a o putea suprascrie in clasa derivata.

   /// <summary>
    /// abstract class implements IPerson interface
    /// </summary>
    public abstract class Child : IPerson
    {
        #region Implementation of IPerson

        public string FirstName
        {
            get { throw new NotImplementedException(); }
            set { throw new NotImplementedException(); }
        }

        public string LastName
        {
            get { throw new NotImplementedException(); }
            set { throw new NotImplementedException(); }
        }

        public int Age
        {
            get { throw new NotImplementedException(); }
            set { throw new NotImplementedException(); }
        }

        public string Add()
        {
            throw new NotImplementedException();
        }

        public string Delete()
        {
            throw new NotImplementedException();
        }

        public virtual int CalculateWage()
        {
            throw new NotImplementedException();
        }

        #endregion
    }

Putem mosteni clasa abstracta Child fara a intampina probleme. De asemenea, putem suprascrie metoda CalculateWage.

    public class Boy : Child
    {
        public override int CalculateWage()
        {
            return 1;
        }
    }

Concluzii:

O interfata nu contine cod pentru implementare, ea contine doar signaturile.
O clasa abstracta poate contine codul complet sau partial, care poate fi rescris.

O interfata nu poate contine ca membri: constante, constructori, destructori, campuri, membri statici sau alte interfete.
Intr-o clasa abstracta se pot defini campuri, constante, metode, proprietati.

Din punct de vedere al vitezei de executie, o interfata este mai lenta decat o clasa abstracta.

Category: Uncategorized
  • mehigh says:

    Și atunci de ce/cînd interfață?

    January 7, 2011 at 11:44 am
  • zeltera says:

    Raspunsul la intrebarea ta nu e simplu, si uneori tine si de stilul tau de programare.

    Eu lucrez cu interfete in momentul in care vreau sa fiu sigur ca anumite clase implementeaza o metoda (de exemplu, IParser – cu metoda Parse – pentru a defini un arbore de clase care pot primi un parametru de un tip pe care sa il transforme, parse, in alt tip). O clasa abstract o folosesc in momentul in care am nevoie de un set de clase de baza (una sau mai multe) pe care sa imi construiesc clasele necesare aplicatiei. Un exemplu clasic e Person, care e abstracta, si din care poti creea clasele Student, Employee, Teacher etc.

    Mai pe scurt spus, daca scopul e sa impui o “interfata”, adica un set de metode, folosesc Interface. Daca scopul este sa dezvolt o baza pe care sa construiesc clase specializate folosesc o clasa abstracta.

    January 7, 2011 at 2:40 pm
  • mehigh says:

    Tocmai citesc într-un chestionar și un alt răspuns: „It makes the code more generic”. :)

    January 7, 2011 at 4:36 pm
  • marius says:

    @Mehigh
    Cred ca depinde de scenarii si de ce anume ai nevoie.
    Daca preferi interfetele, recomandarea este sa nu abuzezi in folosirea lor.

    January 7, 2011 at 6:14 pm
  • mehigh says:

    Ok. Îmi plac articolele tale. Să nu te oprești.

    January 10, 2011 at 10:58 am

Your email address will not be published. Required fields are marked *

*