Willkommen zu diesem kleinen Tutorial über generelle Prinzipien der OOP.
Zuerst einmal Was ist OOP überhaupt ?
Man ließt in vielen Beiträgen Dinge wie
Nehmen wir einmal Folgendes an: Wir möchten ein Programm schreiben, das eine grafische Oberfläche (ne Form) hat und das diese sich beim darauf Klicken schließt. Jetzt gibts zwei Möglichkeiten, entweder wir stellen für alles eine Funktion bereit (CreateWindow, ShowWindow, CloseWindow, RegisterCallback usw.) und müssen damit mühsam ein Fenster erschaffen, oder wir erstellen eine Klasse die all diese Funktionen besitzt und instanzieren damit ein Objekt. Eine Klasse ist vergleichbar mit einem Bauplan. Sie beinhaltet Funktionen, Variablen, Properties usw. Wenn man jetzt ein Objekt dieses Bauplans haben möchte, in unserem Fall die Klasse Form, dann instanziert man eine solche Klasse mit dem Schlüsselwort
Objektorientierte Programmierung lässt sich in drei Bereiche Aufteilen: Vererbung, Kapselung und Polymorphie.
Vererbung
Man nehme folgendes Beispiel: Es gibt die Klasse Tier, diese hat die Methode "Talk()" und "Move()" implementiert. Nun möchten wir Klasse Hund, Katze und Maus implementieren, jetzt müsste man für Hund die 2 Methoden implementieren, für Katze und für Maus, dass ist natürlich viel zu viel Arbeit (merke: Der gute Programmierer ist faul) also schaffte hier die Vererbung abhilfe. Wenn Hund, Katze und Maus nun von Tier erben, haben sie direkt alle Methoden und fertig sind wir.
VB
C#
Soweit so gut. Wollen wir jetzt, dass ein Hund bellt, die Katze Miaut usw. müssen wir die Methode überschreibbar, also virtuell, machen.
VB
C#
Wenn nun Dog.Talk() aufgerufen wird, wird die überschreibende Methode Dog.Talk() aufgerufen und diese ruft dann noch die überschriebene Methode der Basisklasse Animal.Talk() auf (natürlich optional). So kann man mittels Vererbung, Klassen die Methoden und Eigenschaften der Basisklasse übernehmen lassen. In anderen OO Sprachen wie C++ gibt es Mehrfachvererbung, die es einem erlaubt von mehreren Klassen zu erben und auch auf private oder protected oder public Member zuzugreifen. So kann ein Hund ein Tier sein aber auch gleichzeitig ein Haustier. Um soetwas in .Net zu lösen gibt es Interfaces diese sind "Verträge" mit der implementierenden Klasse. Er setzt vorraus das die Klasse sämtliche Methoden des Interfaces implementiert. So sind Interfaces vom Prinzip Klassen mit virtuellen abstrakten Methoden.
Kapselung
Kapselung bietet Schutz für Daten vom äußeren Zugriff. Jeder Hund hat ein Alter, da kann man jetzt gepflegt eine Variable anlegen
Polymorphie
Polymorphie ist die Fähigkeit einer Variablen eines Typs, ein Objekt unterschiedlichen Typs aufnehmen zu können. Bezogen auf unser Beispiel, möchten wir nun eine Collection anlegen in die all unsere Haustiere kommen, bräuchten wir eine List<> für Hund, eine für Katze und eine für Maus. Dazu kommt noch eine Funktion AddAnimal() die zu dieser Collection ein Haustier hinzufügen soll. Diese müsste auch 3mal überladen werden, kommt jetzt noch ein Goldfisch hinzu, so darf man noch ne List und Funktion hinzufügen. Eine beinahe unmenschliche Arbeit. Da alle Haustiere aber von Animal erben, können wir auch einfach eine List<Animal>.
VB
C#
Dies nennt sich dynamische Polymorphie (da es zur Laufzeit geschiet).
Die Polymorphie ermöglicht es auch den Typen eines Objektes zu löschen (folglich genannt Type Erasure)
Dies ist eine Klasse die davon nutzen macht:
VB
C#
C++ (mein eigentliches Orginal)
Das ist der grobe Überblick über die drei Pfeiler der OOP. Bei Fragen oder wichtigen Ergänzungen, möge derjenige sprechen
Grüße
Zuerst einmal Was ist OOP überhaupt ?
Man ließt in vielen Beiträgen Dinge wie
Das ist zwar unglimpflich ausgedrückt, aber meist kommt danach ein Beitrag in dieser RichtungBenutz nicht Split() das ist VB6
So was hat das auf sich ? Objektorientierung heißt, man ist auf Objekte orientiert...Diese Funktion sollte nicht genutzt werden, da sie nicht OO ist.
Nehmen wir einmal Folgendes an: Wir möchten ein Programm schreiben, das eine grafische Oberfläche (ne Form) hat und das diese sich beim darauf Klicken schließt. Jetzt gibts zwei Möglichkeiten, entweder wir stellen für alles eine Funktion bereit (CreateWindow, ShowWindow, CloseWindow, RegisterCallback usw.) und müssen damit mühsam ein Fenster erschaffen, oder wir erstellen eine Klasse die all diese Funktionen besitzt und instanzieren damit ein Objekt. Eine Klasse ist vergleichbar mit einem Bauplan. Sie beinhaltet Funktionen, Variablen, Properties usw. Wenn man jetzt ein Objekt dieses Bauplans haben möchte, in unserem Fall die Klasse Form, dann instanziert man eine solche Klasse mit dem Schlüsselwort
New
. Dim variable As New Form()
Hier ruft das Schlüsselwort New den Konstruktor der Form-Klasse auf und variable wird eine Instanz oder ein Objekt der Klasse Form und kann benutzt werden. Dies erklärt auch warum z.B. Form2.Show() ziemlicher Schwachsinn ist, man greift auf eine Klasse also einen Bauplan zu. Der Bauplan kann keine Funktionen ausführen, er ist nurn Bauplan. Man muss folglich erst mal ein Objekt mit dem Bauplan bauen und dann .Show() aufrufen. Der Konstruktor einer Klasse ist eine Funktion, in dieser kann die Klasse irgendwelche Dinge initialisieren oder alle Möglichen Dinge vorbereiten. Der Konstruktor wird in VB.Net als Sub New()
deklariert und in C# als Methode, die den selben Namen wie die Klasse selbst hat class TestKlasse {TestKlasse() {} // <- Konstruktor};
. Jetzt möchte man aber 2 Fenster haben, entweder man ruft die ganzen Funktionen erneut auf oder man erstellt einfach n neues Objekt unserer Klasse. Die zweite Variente wäre Objektorientiert und nebenbei auch um einiges leichter. Objektorientierte Programmierung lässt sich in drei Bereiche Aufteilen: Vererbung, Kapselung und Polymorphie.
Vererbung
Man nehme folgendes Beispiel: Es gibt die Klasse Tier, diese hat die Methode "Talk()" und "Move()" implementiert. Nun möchten wir Klasse Hund, Katze und Maus implementieren, jetzt müsste man für Hund die 2 Methoden implementieren, für Katze und für Maus, dass ist natürlich viel zu viel Arbeit (merke: Der gute Programmierer ist faul) also schaffte hier die Vererbung abhilfe. Wenn Hund, Katze und Maus nun von Tier erben, haben sie direkt alle Methoden und fertig sind wir.
VB.NET-Quellcode
- Public Class Animal
- Public Sub Move()
- Console.WriteLine("Animal is moving") ' Move()-Methode, die jedes Tier haben soll
- End Sub
- Public Sub Talk()
- Console.WriteLine("Animal is talking") ' Talk()-Methode, damit das Tier auch Laut geben kann
- End Sub
- End Class
- Public Class Dog
- Inherits Animal ' Hier erbt Dog (mittels dem Schlüsselwörtchen Inherits) von Animal und erbt somit alle Methoden
- End Class
- Public Class Cat
- Inherits Animal
- End Class
- Public Class Mouse
- Inherits Animal
- End Class
C-Quellcode
- public class Animal
- {
- public void Move() // Move()-Methode, die jedes Tier haben soll
- {
- Console.WriteLine("Animal is moving");
- }
- public void Talk() // Talk()-Methode, damit das Tier auch Laut geben kann
- {
- Console.WriteLine("Animal is talking");
- }
- };
- public class Dog : Animal {}; // Hier erbt Dog (mittels dem ':') von Animal und erbt somit alle Methoden
- public class Cat : Animal {};
- public class Mouse : Animal {};
Soweit so gut. Wollen wir jetzt, dass ein Hund bellt, die Katze Miaut usw. müssen wir die Methode überschreibbar, also virtuell, machen.
VB.NET-Quellcode
- Public Class Animal
- Public Sub Move()
- Console.WriteLine("Animal is moving")
- End Sub
- Public Overridable Sub Talk()
- Console.WriteLine("Animal is talking") ' Talk() wird nun durch 'Overrideable' als überschreibbar deklariert
- End Sub
- End Class
- Public Class Dog
- Inherits Animal
- Public Overrides Sub Talk() ' Animal.Talk() wird durch die 'Overrides'-Anweisung überschrieben
- Console.WriteLine("Wuff")
- MyBase.Talk() ' Zusätzlich wird die Talk()-Methode der Basisklasse Animal aufgerufen, nachdem der Hund gebellt hat
- End Sub
- End Class
C-Quellcode
- public class Animal
- {
- public void Move()
- {
- Console.WriteLine("Animal is moving");
- }
- public virtual void Talk() // Talk() wird nun durch 'virtual' als überschreibbar (und virtuell) deklariert
- {
- Console.WriteLine("Animal is talking");
- }
- };
- public class Dog : Animal
- {
- public override void Talk() // Animal.Talk() wird durch die 'override'-Anweisung überschrieben
- {
- Console.WriteLine("Wuff");
- base.Talk(); // Zusätzlich wird die Talk()-Methode der Basisklasse Animal aufgerufen, nachdem der Hund gebellt hat
- }
- };
Wenn nun Dog.Talk() aufgerufen wird, wird die überschreibende Methode Dog.Talk() aufgerufen und diese ruft dann noch die überschriebene Methode der Basisklasse Animal.Talk() auf (natürlich optional). So kann man mittels Vererbung, Klassen die Methoden und Eigenschaften der Basisklasse übernehmen lassen. In anderen OO Sprachen wie C++ gibt es Mehrfachvererbung, die es einem erlaubt von mehreren Klassen zu erben und auch auf private oder protected oder public Member zuzugreifen. So kann ein Hund ein Tier sein aber auch gleichzeitig ein Haustier. Um soetwas in .Net zu lösen gibt es Interfaces diese sind "Verträge" mit der implementierenden Klasse. Er setzt vorraus das die Klasse sämtliche Methoden des Interfaces implementiert. So sind Interfaces vom Prinzip Klassen mit virtuellen abstrakten Methoden.
Kapselung
Kapselung bietet Schutz für Daten vom äußeren Zugriff. Jeder Hund hat ein Alter, da kann man jetzt gepflegt eine Variable anlegen
Dim Age As Integer
bzw. int Age;
. Nur jeder kann diese Variable des Objekts verändern und z.B. den Hund verjüngen semantisch ist das natürlich Quatsch und deswegen ists zu verhindern. Hier gibts die Zugriffsmodifizierer public, private und protected. Public lässt öffentlichen Zugriff zu, Private nur Zugriff innerhalb der Klasse und Protected lässt Zugriff aus der selben oder einer erbenden Klasse zu. Zusätzlich gibt es noch Friend, welches Klassen erlaubt auf Private Member zuzugreifen. So können wir die Variable also gepflegt Private oder Protected machen Private Age As Integer
bzw. private int Age;
. Polymorphie
Polymorphie ist die Fähigkeit einer Variablen eines Typs, ein Objekt unterschiedlichen Typs aufnehmen zu können. Bezogen auf unser Beispiel, möchten wir nun eine Collection anlegen in die all unsere Haustiere kommen, bräuchten wir eine List<> für Hund, eine für Katze und eine für Maus. Dazu kommt noch eine Funktion AddAnimal() die zu dieser Collection ein Haustier hinzufügen soll. Diese müsste auch 3mal überladen werden, kommt jetzt noch ein Goldfisch hinzu, so darf man noch ne List und Funktion hinzufügen. Eine beinahe unmenschliche Arbeit. Da alle Haustiere aber von Animal erben, können wir auch einfach eine List<Animal>.
VB.NET-Quellcode
- Dim Animals As New List(Of Animal)
- Public Sub AddAnimal(ByVal anm As Animal) ' Fügt ein Tier zur Haustier-Liste hinzu
- Animals.Add(anm)
- End Sub
- ...AddAnimal(New Dog()) ' AddAnimal() wird mit einem Hund aufgerufen, so wird der Hund zum Tier (Dim hund As Animal = New Dog())
- ...AddAimal(New Cat()) ' So wird die Variable automatisch zum Animal (sofern seine Klasse von Animal erbt oder Animal ist)
C-Quellcode
- List<Animal> Animals = new List<Animal>();
- public void AddAnimal(Animal anm) {Animals.Add(anm);} // Fügt ein Tier zur Haustier-Liste hinzu
- ...AddAnimal(new Dog()); // AddAnimal() wird mit einem Hund aufgerufen, so wird der Hund zum Tier (Dim hund As Animal = New Dog())
- ...AddAnimal(new Cat()); // So wird die Variable automatisch zum Animal (sofern seine Klasse von Animal erbt oder Animal ist)
Dies nennt sich dynamische Polymorphie (da es zur Laufzeit geschiet).
Die Polymorphie ermöglicht es auch den Typen eines Objektes zu löschen (folglich genannt Type Erasure)
Dies ist eine Klasse die davon nutzen macht:
VB.NET-Quellcode
- Class Any
- Private Interface IHoldBase ' Ein leeres Interface, welches hier die Eigentliche Polymorphie übernimmt
- End Interface
- Private Class Holder(Of T) ' Der "DataHolder", welcher den Datentyp per Generic bekommt (T) und IHoldBase implementiert
- Implements IHoldBase
- Public Sub New(val As T)
- data = val ' Variable val wird in data verstaut
- End Sub
- Public Function GetData() As T
- Return data ' Variable wird wieder zurückgegeben, als Typ T
- End Function
- Private data As T
- End Class
- Public Sub SetData(Of T)(val As T) ' Any.SetData(Of T)
- storage = New Holder(Of T)(val) ' Hier schlägt die Polymorphie zu. New Holder(Of T)(val) wird zur IHoldBase
- End Sub
- Public Function GetData(Of T)() As T
- Return DirectCast(storage, Holder(Of T)).GetData() ' storage wird zu T gecastet und zurückgegeben, falls storage nicht vom Typ T ist, kommt ne Exception
- End Function
- Private storage As IHoldBase
- End Class
- Dim i As New Any()
- i.SetData(Of Integer)(8) ' Ein Integer vom Typ IHoldBase (verstaut im Objekt i der Klasse Any)
- MessageBox.Show(i.GetData(Of Integer)().ToString()) ' IHoldBase wird zum Holder(Of Integer) und die Variable wird zurückgegeben.
C-Quellcode
- class Any
- {
- private interface IHoldBase { }; // Ein leeres Interface, welches hier die Eigentliche Polymorphie übernimmt
- private class Holder<T> : IHoldBase // Der "DataHolder", welcher den Datentyp per Generic bekommt (T) und IHoldBase implementiert
- {
- public Holder(T val) {data = val;} // Variable wird in data verstaut
- public T GetData() { return data; } // Variable wird wieder zurückgegeben, als Typ T
- private T data;
- };
- public void SetData<T>(T val) { storage = new Holder<T>(val); } // Hier schlägt die Polymorphie zu. new Holder<T>(val) wird zur IHoldBase
- public T GetData<T>()
- {
- return ((Holder<T>)storage).GetData(); // storage wird zu T gecastet und zurückgegeben, falls storage nicht vom Typ T ist, kommt ne Exception
- }
- private IHoldBase storage;
- };
- Any i = new Any();
- i.SetData<int>(8); Ein int vom Typ IHoldBase (verstaut im Objekt i der Klasse Any)
- MessageBox.Show(i.GetData<int>().ToString()); // IHoldBase wird zum Holder<int> und die Variable wird zurückgegeben.
C-Quellcode
- class any
- {
- private:
- class hold_base {public: virtual ~hold_base() {};}; // Interface hold_base als polymorpher Typ
- template <typename T>
- class data_holder : public hold_base // data_holder<> stored eine Variable vom Typ T und gibt diese auch wieder zurück
- {
- public:
- data_holder(const T& val) : data(val) {};
- const T& get_data() const {return data;};
- private:
- const T data;
- };
- public:
- template <typename T>
- any(const T& val) : storage(new data_holder<T>(val)) {}; // Template-Konstruktor initialisiert hold_base* storage mit T const& val (aufm Heap)
- template <typename T>
- const T& get_data() const
- {
- data_holder<T>* holder = dynamic_cast<data_holder<T>*>(storage.get()); // storage wird zu data_holder<T> gecastet bei Erfolg zurückgegeben, bei Misserfolg wird eine Exception geschmissen
- if(holder)
- return holder->get_data();
- else
- throw invalid_argument("Template type isn't equal to the stored type");
- };
- private:
- shared_ptr<hold_base> storage; // hold_base* storage hier als SmartPointer damit man die Daten nicht selbst löschen muss
- };
- int main(int argc, char* argv[])
- {
- int f = 9;
- any i(f); // bzw. any<int> i(f);
- any s(string("Hallo")); // bzw. any<string> f("Hallo");
- try
- {
- cout << i.get_data<int>() << endl; // Gibt "9\n" aus
- cout << s.get_data<string>() << endl; // Gibt "Hallo\n" aus
- cout << i.get_data<long>() << endl; // i beinhaltet ein int, also wird eine Exception geworfen
- }
- catch (const exception& e)
- {
- cerr << e.what() << endl;
- }
- return 0;
- }
Das ist der grobe Überblick über die drei Pfeiler der OOP. Bei Fragen oder wichtigen Ergänzungen, möge derjenige sprechen
Grüße
Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Gonger96“ ()