07. Abstrakcja w C#

07. Abstrakcja w C#

Autor: Paweł Kruczkowski

Opublikowano: 11/7/2006, 12:00 AM

Liczba odsłon: 84454

W poprzednim tygodniu opowiedzieliśmy sobie o dwóch bardzo ważnych pojęciach jakimi niewątpliwie są: dziedziczenie oraz polimorfizm. Wprowadziliśmy sobie również definicje metod wirtualnych i przesłaniających, a także poznaliśmy nowe, ważne słówko kluczowe: base.

Dzisiaj na pewno każdy z nas potrafiłby wykorzystać swoją wiedzę na powyższe tematy, dlatego też czym prędzej przejdźmy do przykładu:

public class Figura
{
    protected double a;
    protected double b;
 
    public Figura(double a, double b)
    {
        this.a = a;
        this.b = b;
    }
 
    public virtual double PoleFigury()
    {
        System.Console.WriteLine("Niezdefiniowana figura!");
        return 0;
    }
}
 
public class Prostokat : Figura
{
    public Prostokat(double a, double b) : base(a, b)
    {  }
    public override double PoleFigury()
    {
        double pole = a * b;
        System.Console.WriteLine("Obliczamy pole prostokąta.");
        return pole;
    }
}
 
public class Trojkat : Figura
{
    public Trojkat(double a, double b) : base(a, b)
    { }
    public override double PoleFigury()
    {
        double pole = (a * b) / 2;
        System.Console.WriteLine("Obliczamy pole trójkąta.");
        return pole;
    }
}
 
class Glowna
{
    static void Main(string[] args)
    {
        //deklarujemy odpowiednie obiekty wraz z wartosciami ich zmiennych
        Figura f = new Figura(5, 6);
        Prostokat p = new Prostokat(7, 8);
        Trojkat t = new Trojkat(9, 10);
        //wywolujemy metode obliczajaca pole odpowiedniej figury
        System.Console.WriteLine("Pole wynosi więc: {0}", f.PoleFigury());
        System.Console.WriteLine("Pole wynosi: {0}", p.PoleFigury());
        System.Console.WriteLine("Pole wynosi: {0}", t.PoleFigury());
         
    }
}

Powyższy przykład jest dla nas przypomnieniem podstawowej wiedzy na temat dziedziczenia oraz polimorfizmu. Po skompilowaniu otrzymamy następujące wyniki:

Powyższy program jest bardzo dobrym przykładem, aby wytłumaczyć sobie pewną sytuację. A mianowicie taką, w której chcielibyśmy zadeklarować klasę bazową, która to z kolei byłaby wspólna dla pozostałych (klasa Prostokat oraz klasa Trojkat dziedziczą po klasie bazowej: Figura, a więc Figura jest dla pozostałych wspólna) oraz dawałaby możliwość uzupełnienia szczegółów (metoda PoleFigury() w klasie Figura nie powinna obliczać żadnego pola, a jedynie powinna zostać odziedziczona przez klasy pochodne od klasy Figur i tam dopiero obliczać odpowiednie wartości). Mówiąc jaśniej, rozważmy klasę Prostokat w powyższym kodzie. W klasie tej przesłoniliśmy odziedziczoną od klasy Figura metodę PoleFigury(), która w tej klasie oblicza po prostu pole prostokąta. Dla tej klasy nie ma znaczenia, że metoda PoleFigury() nie jest wcześniej zdefiniowana (w naszym przypadku w klasie Figura). Ma natomiast znaczenie, aby klasa pochodna przesłoniła wszystkie konieczne metody (w tym przypadku właśnie metodę obliczającą pole prostokąta). Aby uzyskać taki mechanizm musimy użyć klas oraz metod abstrakcyjnych.

Innymi słowy, aby wymusić utworzenie nowej wersji metody klasy bazowej, należy tę metodę oznaczyć jako metodę abstrakcyjną. Metoda taka nie zawiera kodu, a jedynie w swej definicji ma: nazwę oraz sygnaturkę. Dopiero w klasach pochodnych należy dodać do takiej metody kod. Jeżeli klasa posiada co najmniej jedną metodę abstrakcyjną, to taką klasę nazywamy klasą abstrakcyjną.

Aby zadeklarować metodę musimy użyć modyfikatora: abstract w następujący sposób:

abstract public void MojaMetoda();

Ponieważ metoda abstrakcyjna nie posiada kodu, po jej sygnaturze nie ma nawiasów klamrowych, a jej definicja kończy się średnikiem.

Aby zadeklarować klasę abstrakcyjną, również używamy słówka: abstract jak poniżej:

 

abstract class MojaKlasa

            {  }

Jak zostało wyżej napisane, klasa abstrakcyjna stanowi jak gdyby bazę metod dla wszystkich jej klas pochodnych, ale – co bardzo ważne – w programie nie wolno nam tworzyć instancji tej klasy. Próba zdefiniowania obiektu klasy abstrakcyjnej zakończy się niepowodzeniem, dlatego też, jeśli jakaś klasa zawiera choć jedną metodę abstrakcyją, to już nie będziemy mogli się do niej odwoływać przez obiekt tej klasy a jedynie poprzez mechanizm dziedziczenia oraz polimorfizmu.

Prześledźmy poniższy przykład:

abstract public class Pierwsza
{
    abstract public void Abstrakcja();
    
    public void Rzeczywistosc()
    {
        System.Console.WriteLine("Moja najzwyklejsza metoda.");
    }
}
 
public class Druga : Pierwsza
{
    public override void Abstrakcja()
    {
        System.Console.WriteLine("Moja pierwsza przesłonięta metoda abstrakcyjna!");
    }
}
 
class Glowna
{
    static void Main(string[] args)
    {
        Druga dr = new Druga();
        dr.Abstrakcja();
        dr.Rzeczywistosc();
    }
}

Klasa Pierwsza implementuje abstrakcyjną metodę Abstrakcja(), która w swej definicji posiada jedynie nazwę oraz sygnaturkę. Klasa ta posiada więc co najmniej jedną abstrakcyjną metodę, stąd jest klasą abstrakcyjną. Klasę tę dziedziczy klasa Druga, w której przesłaniamy metodę abstrakcyjną i dodajemy jej kod. Zauważmy, że w powyższym przykładzie nie ma definicji obiektu klasy pierwszej, ponieważ klasa ta jest abstrakcyjna. Drugi ważny wniosek do zapamiętania: klasa pochodna po klasie abstrakcyjnej musi przesłonić wszystkie metody abstrakcyjne jakie ona posiada.

Po uruchomieniu powyższego programu otrzymamy następujące wyniki:

Znając definicję oraz sposób deklarowania klas oraz metod abstrakcyjnych, spróbujmy udoskonalić pierwszy przykład z dzisiejszego artykułu poprzez zdefiniowanie między innymi klasy Figura jako klasy abstrakcyjnej:

//deklaracja abstrakcyjnej klasy
abstract public class Figura
{
    protected double a;
    protected double b;
 
    public Figura(double a, double b)
    {
        this.a = a;
        this.b = b;
    }
 
    //deklaracja abstrakcyjnej metody
    abstract public double PoleFigury();
}
 
public class Prostokat : Figura
{
    public Prostokat(double a, double b)
        : base(a, b)
    { }
    //przesłoniece abstrakcyjnej klasy
    public override double PoleFigury()
    {
        double pole = a * b;
        System.Console.WriteLine("Obliczamy pole prostokąta.");
        return pole;
    }
}
 
public class Trojkat : Figura
{
    public Trojkat(double a, double b)
        : base(a, b)
    { }
    //przesloniecie abstrakcyjnej metody
    public override double PoleFigury()
    {
        double pole = (a * b) / 2;
        System.Console.WriteLine("Obliczamy pole trójkąta.");
        return pole;
    }
}
 
class Glowna
{
    static void Main(string[] args)
    {
        //deklarujemy odpowiednie obiekty wraz z wartosciami ich zmiennych
        Prostokat pr = new Prostokat(5, 6);
        Prostokat p = new Prostokat(7, 8);
        Trojkat t = new Trojkat(9, 10);
        //wywolujemy metode obliczajaca pole odpowiedniej figury
        System.Console.WriteLine("Pole wynosi więc: {0}", pr.PoleFigury());
        System.Console.WriteLine("Pole wynosi: {0}", p.PoleFigury());
        System.Console.WriteLine("Pole wynosi: {0}", t.PoleFigury());
    }

}

Klasa Figura stała się klasą abstrakcyjną, która posiada abstrakcyjną metodę: PoleFigury(), która to dopiero w klasach pochodnych zostanie odpowiednio zaimplementowana do obliczania pól odpowiednich figur. Oczywiście nie jest możliwe utworzenie obiektu klasy abstrakcyjnej, gdyż kompilator zwróciłby nam błąd. Po prawidłowym skompilowaniu orztymamy następujące wyniki:

W dzisiejszym artykule powiedzieliśmy sobie o klasach i metodach abstrakcyjnych, które ściśle wiążą się z mechanizmami dziedziczenia oraz polimorfizmu. Dowiedliśmy, że klasy abstrakcyjne nie są jedynie sztuczką programowania obiektowego, a reprezentują w pełni tego słowa znaczeniu „abstrakcję”, która umożliwia utworzenie kontraktu dla wszystkich klas pochodnych. Innymi słowy, klasy abstrakcyjne są opisem publicznych metod klas pochodnych, które to rozwijają abstrakcję. Alternatywą dla klas abstrakcyjnych są interfejsy, o których już niedługo sobie opowiemy. Za tydzień natomiast będziemy kontynuować temat klas w języku C# 2.0.

Jak wykorzystać Copilot w codziennej pracy? Kurs w przedsprzedaży
Jak wykorzystać Copilot w codziennej pracy? Kurs w przedsprzedaży

Wydarzenia