Forum: PC-Programmierung OOP & Klassen, aber wie?


von Tim (Gast)


Lesenswert?

Momentan unternehme ich erste Programmierschritte in Python und denke, 
die eigentlichen Grundlagen verstanden zu haben, einschließlich OOP, 
Klassen und Methoden.
Was ich aber überhaupt nicht verstehe: Woher weiß ich wie ich meinen 
Code in welche Klassen und Methoden zu unterteilen habe?

(ich möchte in Kodi ein Video-Addon programmieren, das vom Umfang her 
kaum mehr als Skripte sind, ausgedruckt weniger als ein Dutzend DIN 
A4-Seiten. Bis jetzt ist das alles nur mit Funktionen programmiert, das 
funktioniert, scheint aber unschön zu sein. Als Vorlage dient 
https://github.com/romanvm/plugin.video.example).

von Matthias S. (da_user)


Lesenswert?

Tim schrieb:
> Was ich aber überhaupt nicht verstehe: Woher weiß ich wie ich meinen
> Code in welche Klassen und Methoden zu unterteilen habe?

Erfahrung.

Prinzipiell sollen die "tiefsten" Klassen und Methoden möglichst 
unspezifisch sein. Mit jeder weiteren Schicht wirds dann spezifischer.
Auch sollte die sich das, was eine Methode tut mit einem möglichst 
passenden Verb beschreiben lassen - welches dann auch für den 
Methodennamen verwendet werden soll.
Also statt
1
doWork()
lieber
1
read()
2
parse()
3
sort()
4
write()

Hintergründe sind einerseits, dass man unspezifischere Klassen & 
Methoden evtl. für was anderes wiederverwenden kann.
Und es vereinfacht die Fehlersuche. Wenn z.B. bei obigen Beispiel die 
Werte nicht richtig sortiert sind, sucht man in der "sort" Methode. 
Wahrscheinlich bequemer als 3/4 der "doWork" Methode durchzuarbeiten.

BTW: Buchempfehlung: Weniger schlecht programmieren - von Katarina 
Passig u.A.

: Bearbeitet durch User
von was (Gast)


Lesenswert?

Tim schrieb:
> Bis jetzt ist das alles nur mit Funktionen programmiert, das
> funktioniert, scheint aber unschön zu sein.

Also hast du funktionierenden Code, den du jetzt schöner machen willst? 
Das ist doch eine dankbare Ausgangsituation zum lernen.

Hast du mehrere nahezu identische Codeabschnitte? -> In eine Methode 
packen, die kleinen Unterschiede über Parameter handeln.

Tim schrieb:
> Woher weiß ich wie ich meinen
> Code in welche Klassen und Methoden zu unterteilen habe?

Wie Matthias sagt, Erfahrung. Es gibt nicht die eine Schritt-für-Schritt 
Anleitung. Das Ziel sollte eben sein, dass du den Überblick nicht 
verlierst und auch in ein paar Monaten wieder verstehst, wie dein Code 
funktioniert.

Das heißt nicht unbedingt, dass man sich auf Zwang irgendwelche Klassen 
aus den Fingern saugen muss.
Eine Klasse die nichts repräsentiert ist das Äquivalent zur 
Krimskrams-Schublade im echten Leben. Sieht aus als hätte man 
aufgeräumt, macht Dinge wiederzufinden aber nicht einfacher.

von Heiner (Gast)


Lesenswert?

Tim schrieb:
> Was ich aber überhaupt nicht verstehe: Woher weiß ich wie ich meinen
> Code in welche Klassen und Methoden zu unterteilen habe?

3 Antworten dazu:

1. Python selbst stellt hier überhaupt keine Anforderungen. Das gehört 
zu den schönen Eigenschaften dieser Sprache: Ob du Klassen (oder auch 
überhaupt Funktionen) bildest, bleibt dir grundsätzlich selbst 
überlassen.

2. Sofern man auf Bibliotheken oder allgemein bereits vorhandenen Code 
aufbaut, könnte man gezwungen sein, mit Klassen zu hantieren, z.B. von 
einer bereits existierenden Klasse zu erben und dann bestimmte Methoden 
zu überschreiben. Ein einigermaßen einfaches Beispiel findest du bei 
exceptions: Wenn du eine eigene exception haben willst, musst du selbst 
etwas von der Klasse Exception (oder BaseException, aber meistens die 
falsche Wahl ...) ableiten.

3. Dann bleibt noch die Situation, dass du selbst objektorientiert 
schreiben willst. Dazu schaust du dir am besten mal Bücher zum Thema 
objektorientierte Analyse und Design an, außerdem viel möglichst realen 
Code. Ein gutes Gespür, wie fein oder grob Klassen gebildet werden 
sollten, entwickelst du nur mit Erfahrung.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Tim schrieb:
> Was ich aber überhaupt nicht verstehe: Woher weiß ich wie ich meinen
> Code in welche Klassen und Methoden zu unterteilen habe?

Wie schon jemand schrieb, Erfahrung. Aber nehmen wir mal die ganz alte 
Schule:

Klassen sind Baupläne für Objekte.

Objekte sind Instanzen einer Klasse.

Objekte repräsentieren (virtuell) reale oder abstrakte Dinge.

An dem Punkt kommen dann so typische Beispiele für Objekte wie Auto, 
Reifen, Rechteck, Linie, Server, Geldautomat. Praktisch heißt das, du 
siehst dir dein Problem an und arbeitest heraus welche Dinge bei deinem 
Problem eine Rolle spielen. Danach bildest du die Klassen.

Objekte kann man bitten für das Objekt typische Dinge zu tun oder Fragen 
über sich zu beantworten. Beispiel: Auto->anfahren(), Auto->bremsen(), 
Auto->welcheFarbeHastDu().

Was ein Objekt tun kann und auf welche Fragen es antworten kann wird 
durch Methoden beschrieben.

Also bekommen deine Klassen (die Baupläne für Objekte sind), jeweils die 
Methoden die für die jeweiligen Objekte typisch sind und die für die 
konkrete Problemlösung nötig sind.

Das "für das Objekt typisch" ist dabei wichtig. Viele OO-Programme 
halten sich nicht dran und es kommt Müll raus bei dem Klassen/Objekte 
nur zufällige Gruppierungen von Methoden sind.

Ebenso ist "für die konkrete Problemlösung" wichtig. Beispiel: Wenn dein 
virtuelles Auto nur fahren und anhalten können muss wirst du, weil es 
einfacher ist, keine Methoden zum Blinken oder zum Betätigen der Fenster 
schreiben.

Es geht, bis auf Ausnahmen, nicht darum ein reales oder abstraktes Ding 
vollständig im Rechner abzubilden. Ausnahmen siehst du bei Bibliotheken 
und Frameworks. Die müssen mehr anbieten als in einer einzelnen 
Anwendung benötigt wird um möglichst viele Anwendungen zu unterstützen.

von vn nn (Gast)


Lesenswert?

Tim schrieb:
> Was ich aber überhaupt nicht verstehe: Woher weiß ich wie ich meinen
> Code in welche Klassen und Methoden zu unterteilen habe?

Für einfache Projekte kann es helfen, als Text auszuformulieren was man 
möchte:
Ich habe ein Auto. Ein Auto ist ein Sonderfall eines Kraftfahrzeuges. 
Ein Kraftfahrzeuges hat eine Motorleistung und kann fahren (vorwärts und 
rückwärts), lenken (rechts und links) und bremsen. Um fahren zu können, 
braucht es mindestens zwei Achsen mit jeweils mindestens einem Rad, 
mindestens eine Achse lenkbar. Ein Auto hat zwei Achsen mit zwei Rädern, 
eine davon lenkbar. Im Gegensatz zum generischen Kraftfahrzeug hat es 
auch eine Karosserie, mit Lack in einer bestimmten Farbe.

Kraftfahrzeug:
 - Eigenschaften: Leistung
 - Member: Achsen[]
 - Methoden: fahre(richtung), lenke(richtung), bremse()

Achse:
 - Member: Räder

LenkbareAchse (erbt von Achse):
 - Eigenschaften: Ausrichtung
 - Member: Räder[]
 - Methoden: lenke(richtung)

Auto (erbt von Kraftfahrzeug, implementiert Karosserie):
 - Eigenschaften: Leistung, Farbe
 - Member: Achsen[2] = { Lenkbare Achse(), Achse() }, Karosserie
 - Methoden: fahre(richtung), lenke(richtung), bremse()

Wüstenbuggy (erbt von Kraftfahrzeug):
 - Eigenschaften: Leistung
 - Member: Achsen[2] = { Lenkbare Achse(), Achse() }
 - Methoden: fahre(richtung), lenke(richtung), bremse()

Radlader (erbt von Kraftfahrzeug, implementiert Karosserie):
 - Eigenschaften: Leistung, Farbe
 - Member: Achsen[2] = { LenkbareAchse(), LenkbareAchse() }, Schaufel
 - Methoden: fahre(richtung), lenke(richtung), bremse(), 
hebeSchaufel(richtung)

Karosserie:
 - Eigenschaften: Farbe


--
Praktikabilität dieser Methode hängt vom Anwendungsfall ab.

von PittyJ (Gast)


Lesenswert?

Das kommt durch Erfahrung und jahrelanges Programmieren.
Erst dann kann man Abschätzen, ob die Klassenstruktur hilfreich ist oder 
mehr Probleme bei Änderungen macht.
Dazu gehört dann auch die Reflektion: "Dieses Konstrukt, was ich vor 5 
Jahren gemacht habe, war vollkommen überdimensioniert".
Bei nächsten Mal macht man es einfacher.

Ich habe z.B. bei Kollegen eine Vererbungsstruktur mit 5 Ebenen gesehen. 
Da blickt heute keiner mehr durch.
Aber das muss man erst einmal selber erleben.

von Tim (Gast)


Lesenswert?

Danke für die bisherigen Kommentare.
Welches Buch/Tutorial mit praxisnahen Beispielen käme noch infrage?
Geplante Anwendung --> umgesetzte Klassenstruktur

von Matthias S. (da_user)


Lesenswert?

Wenn ich das richtig gelesen habe, willst du ja "nur" ein Kodi-PlugIn 
programmieren, bist also nicht im professionellen Umfeld unterwegs?

Dann mach dir nicht zu viele Gedanken, leg einfach mal los. Wenn du 
klein anfängst und dein Projekt nach und nach wächst, wird das auch mit 
der Klassenstruktur. Du könntest ggf. auch verraten, was du vorhast?

Auch

Matthias S. schrieb:
> Hintergründe sind einerseits, dass man unspezifischere Klassen &
> Methoden evtl. für was anderes wiederverwenden kann.

kannst du nach hinten anstellen.
Wenn du etwas aus einem Hobbyprojekt für ein anderes Projekt verwenden 
kannst gibt es folgende Möglichkeiten:

* du erinnerst dich nicht mehr daran, es schon gemacht zu haben und 
machst es neu
* du musst es sowieso anpassen, also reicht blödes Copy&Paste
* jemand anderes hat es schon besser gemacht und auf Git od. ähnl. 
veröffentlicht

Solltest du dich im prof. Umfeld bewegen, ist man wahrscheinlich sowieso 
gut beraten, sich dem existierenden aktuellen Umfeld anzupassen.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

vn nn schrieb:
> Für einfache Projekte kann es helfen, als Text auszuformulieren was man
> möchte:

Ich will Kreise und Ellipsen zeichnen. Ein Kreis is ein sonderfall einer 
Ellipse. Moment mal, oh, shit: 
https://de.wikipedia.org/wiki/Kreis-Ellipse-Problem

von Dirk K. (merciless)


Lesenswert?

🐧 DPA 🐧 schrieb:
> vn nn schrieb:
>> Für einfache Projekte kann es helfen, als Text auszuformulieren was man
>> möchte:
>
> Ich will Kreise und Ellipsen zeichnen. Ein Kreis is ein sonderfall einer
> Ellipse. Moment mal, oh, shit:
> https://de.wikipedia.org/wiki/Kreis-Ellipse-Problem

Der TO steigt gerade in die OOP ein und du kommst
hier mit einem der kompliziertesten Probleme der
OOP daher, ganz großes Kino.

@TO Einfach mal anfangen, aus Fehlern lernt man.

merciless

von 🐧 DPA 🐧 (Gast)


Lesenswert?

Die Idee hinter OOP ist ja schön, es erscheint Intuitiv, Dinge zu 
Klassifizieren, und sich zu überlegen x ist ein y, y ist ein z, etc. 
Aber Ich denke es ist wichtig von Anfang an zu verstehen, das sich eben 
nicht alles sauber auf die Weise Abbilden lässt.

In punkto Programmstrukturen baut OOP auf 3 Dinge auf:
 1) Das Kapseln/Zusammenfassen zusammengehörender Daten (Objekte)
 2) Das assoziieren auf diese anwendbarer Funktionen zu diesen 
(Methoden)
 3) Erweitern bestehender Objekte, sowie das Überschreiben derer 
Methoden und Eigenschaften. (Vererbung)

1 Würde ich zumindest bei so ziemlich allen nicht rein funktionalen 
Sprachen als essenziell betrachten.
2 Kann vor allem zusammen mit 3 sehr nützlich sein.
Aber 3 kann halt auch manchmal mehr Probleme bereiten, als es löst.

Der Zweck von dem ganzen OOP zeug ist ja eigentlich, auf intuitivem Weg 
Code dublizierung zu vermeiden, und die Codequalität und Zuverlässigkeit 
des Programms zu erhöhen. Das kann man damit oft auch recht gut machen. 
Das blöde daran ist halt nur, wenn man nicht aufpasst und es übertreibt, 
kann man sich schnell in eine Ecke designen, und grobe Designprobleme 
sind dann oft viel schwieriger zu lösen, als ein paar flache Code Bugs / 
Fehlfunktionen. Und nur um Code zusammenzufassen und übersichtlich zu 
halten, braucht man nicht unbedingt das ganze OOP und Vererbung. 
(Stichwort gemeinsamen Code in Unterfunktionen auslagern.)

Zudem bieten Sprachen heute noch unzählige andere Sprachkonstrukte, die 
auch ergänzend wirken können, und oft der stumpfen Vererbung vorzuziehen 
sind. Außerdem haben viele gute Programmierparadigmen und Strukturen, 
die mit OOP in Verbindung gebracht werden, damit eigentlich gar nichts 
zutun und können auch ohne verwendet werden.

Da ich schon mal dabei bin, will ich gleich mal Werbung für 
(Objekt-)Interfaces machen. Damit kann man angeben, man ist Interessiert 
an Daten/Objekten, mit denen man gewisse Dinge machen kann, und man kann 
angeben, bei welchen Arten von Daten/Objekten das gemacht werden kann. 
(Implementieren muss man es jeweils natürlich trotzdem noch). Oft 
braucht man dann Vererbung nicht mehr, und ist viel flexibler, weil man 
sich nicht mehr überlegen muss, was ist was, sondern einfach sagen kann, 
was kann momentan was.

Die meisten restlichen Vererbungs use-cases kann man auch loswerden, 
indem man eine ist-ein Beziehung durch eine hat-ein Beziehung ersetzt. 
Dass kann dann auch Probleme lösen, z.B. wenn unklar ist, ob X wirklich 
Y ist, wenn unabhängige Objekteigenschaften kollidieren, und vieles 
mehr.

Am Ende bleiben eigentlich nicht mehr viele Fälle übrig, in denen 
Vererbung tatsächlich die beste Lösung ist. Unglücklicherweise ist es in 
OOP Kreisen aber üblich, alles ins OOP Korsett zu zwängen, was anderes 
wäre ja falsch und schlechter Stil. Am Ende muss man sich meistens dann 
halt dem anpassen, was im jeweiligen dem Gebiet üblich ist.

Es gibt immer viele gute Lösungen, und meistens nicht nur den einen 
richtigen Weg. Man muss nur schauen, dass was man verzapft möglichst 
einfach Verständlich sowie von anderen erweiterbar/wartbar ist, und dass 
es auch zuverlässig Funktioniert. In der Regel ist am Ende weniger oft 
mehr.

von Heiner (Gast)


Lesenswert?

... und wenn man nicht aus allem einen Glaubenskrieg und eine 
philosophische Dissertation machen muss, ist OOP einfach nur ein 
praktisches Werkzeug, das bei korrekter Anwendung die gewünschten 
Ergebnisse liefert.

Das Kreis-Ellipse-Problem basiert übrigens zu mindestens 90 % auf der 
übertrieben vereinfachten Vorstellung, dass Vererbung mit einer "ist 
ein"-Beziehung in natürlicher Sprache gleichzusetzen ist. Sobald man 
das verstanden hat, ist es kein "Problem" mehr, eher ein lehrreiches 
Beispiel, warum man sich bei der Modellierung weniger auf anschauliche 
Vorstellungen als auf die tatsächliche Schnittstelle, die eine Klasse 
definiert, konzentrieren sollte.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Tim schrieb:
> Welches Buch/Tutorial mit praxisnahen Beispielen käme noch infrage?
> Geplante Anwendung --> umgesetzte Klassenstruktur

Eins vorweg:

Erst einmal solltest du dir dessen bewusst sein, dass Objektorientierung
in Python kein Dogma ist.

Python unterstützt neben der objektorientierten auch die prozedurale und
ein Bisschen die funktionale Programmierung. Guten Python-Code zeichnet
aus, dass er jedes der genannten Paradigmen dort und nur dort einsetzt,
wo es auch logisch sinnvoll ist.

Anders als bspw. in Java ist es in Python absolut verpönt, jedes noch so
kleine Detail in eine eigene Klasse zu wickeln, nur um irgendwelchen
OOP-Grundsätzen Genüge zu tun.

Es gibt viele guten Python-Programme, die ohne oder mit nur ganz wenigen
selbstgeschriebenen Klassen auskommen.

Andererseits ist es natürlich unklug, dort, wo die die OOP sinnvoll
einsetzbar ist, auf ihre Möglichkeiten und Vorteile zu verzichten.


Ich habe zwar noch nie ein Python-Buch gelesen, aber nach kurzem
Überfliegen erscheint mir das folgende ganz passend zu sein:

  "Python 3 Object-Oriented Programming" von Dusty Phillips

Du kannst ja auch mal einen Blick hineinschmeißen, um zu sehen, ob es
dir zusagt.


Zum Schluss noch ein kurzer Tipp, wie du für den Anfang ganz leicht
potentielle Objekte in deinem Programm finden kannst:

Wenn du das Programm logisch in mehrere Funktionen aufgeteilt hast und
dann feststellst, dass du häufig das Schlüsselwort "global" benutzen
musstest, oder dass mehrere der Funktionen gemeinsame Argumente haben,
ist das ein starkes Indiz dafür, dass die globalen Variablen (oder ein
Teil davon) bzw. die gemeinsam genutzten Funktionsargumente, jeweils
zusammen mit den Funktionen, die sie verwenden, ein Objekt bilden.

Das ist natürlich nur ein ganz kleiner Aspekt der OOP. Schwieriger wird
es u.a. bei der Frage nach einer optimalen Vererbungshierarchie bzw.
wann Vererbung überhaupt sinnvoll ist.

von Pandur S. (jetztnicht)


Lesenswert?

Der Beginn liegt bei den Fundamenten der OOP. Dort wo Daten und Code 
zusammengehoeren, kann man die mit Klassen verbinden.
Das ist schon sehr effizient.
Vererbung kommt sehr viel speaeter, wenn in einem Projekt auffaellt, 
dass man aehnliche Klassen bildet.

von Sheeva P. (sheevaplug)


Lesenswert?

🐧 DPA 🐧 schrieb:
> Die Idee hinter OOP ist ja schön, es erscheint Intuitiv, Dinge zu
> Klassifizieren, und sich zu überlegen x ist ein y, y ist ein z, etc.
> Aber Ich denke es ist wichtig von Anfang an zu verstehen, das sich eben
> nicht alles sauber auf die Weise Abbilden lässt.
>
> In punkto Programmstrukturen baut OOP auf 3 Dinge auf:
>  1) Das Kapseln/Zusammenfassen zusammengehörender Daten (Objekte)
>  2) Das assoziieren auf diese anwendbarer Funktionen zu diesen
> (Methoden)
>  3) Erweitern bestehender Objekte, sowie das Überschreiben derer
> Methoden und Eigenschaften. (Vererbung)

Dr. Alan Kay, der die Objektorientierung und das Fachwort dafür erfunden 
hat, sagt dazu das Folgende: "OOP to me means only messaging, local 
retention and protection and hiding of state-process, and extreme 
late-binding of all things." [1]

Zusammengehörige Daten kann ich auch ohne OOP zusammenfassen, dazu 
bieten klassische Programmiersprachen wie C Strukturen (struct) und 
Pascal seine Records. Aber diese Sprachen bieten sprachseitig keine 
Möglichkeit, die Funktionen, die sie verarbeiten sollen, an ihre 
Datenstrukturen zu binden. Mit ein bisschen Gehacke kann man etwas 
Ähnliches auch in vielen nicht-OO-Sprachen wie C abbilden, aber nur sehr 
unelegant; IMHO gehören Deine ersten beiden Punkte jedenfalls direkt und 
sehr eng zusammen.

Die Vererbung ist eine andere Geschichte und eine Folge aus dem zuvor 
Gesagten, und für die Vererbung ist es teilweise richtig zu sagen, daß 
sie der Deduplizierung von Code dienen. Im Grunde dient die Vererbung 
aber zunächst dem Ziel, ähnliche Objekte zusammenzufassen und sie 
typsicher zu machen. In dem beliebten Auto-Beispiel heißt das, daß ich 
eine Klasse Fahrzeug mit einer abstrakten Methode beschleunige() habe, 
und davon dann meine Klassen Auto und Motorrad ableite. Wenn nun die 
Businessjungs sagen, "beschleunige das Ding", dann kann ich einfach die 
Methode beschleunige() autrufen, und zwar unabhängig davon, ob das 
betreffende Dingsi jetzt ein Auto oder ein Motorrad ist.

> Der Zweck von dem ganzen OOP zeug ist ja eigentlich, auf intuitivem Weg
> Code dublizierung zu vermeiden, und die Codequalität und Zuverlässigkeit
> des Programms zu erhöhen. Das kann man damit oft auch recht gut machen.

Der tiefere Sinn der OOP ist IMHO, den Code übersichtlicher zu 
organisieren und die Daten sowie die Funktionen, die sie bearbeiten, 
zusammenzufassen. Damit kann man auch die Duplizierung von Code 
vermeiden, aber das ist nicht das Ziel.

> Das blöde daran ist halt nur, wenn man nicht aufpasst und es übertreibt,
> kann man sich schnell in eine Ecke designen, und grobe Designprobleme
> sind dann oft viel schwieriger zu lösen, als ein paar flache Code Bugs /
> Fehlfunktionen.

Ich weiß, diese Kritik an der OOP ist nicht neu, und  war angesichts 
jener Zeit vor zwanzig Jahren, als "Objektorientierung" ein 
Verkaufsargument für manche Scharlatane war und es auch noch nicht so 
sehr viel Erfahrung mit dem Konzept gab, durchaus auch berechtigt. Aber 
es erscheint mir etwas widersinnig, jemandem ein neues Paradigma 
nahezubringen, indem man vor historischen (und teilweise hysterischen" 
Irrtümern warnt. Fakt ist: die saubere Strukturierung von Code und Daten 
ist eine Sache der Erfahrung, der Praxis, abstraktem Denkvermögen und 
der Fähigkeit, sein Problem vor und während der Umsetzung immer wieder 
aus der Vogelperspektive zu betrachten. Das gilt allerdings nicht nur 
für OOP, sondern auch für imperative, prozedurale, und funktionale 
Programmierung.

> Und nur um Code zusammenzufassen und übersichtlich zu
> halten, braucht man nicht unbedingt das ganze OOP und Vererbung.

Das ist ein bisschen... halbrichtig. Um die OOP sinn- und nutzbringend 
einsetzen zu können, muß man sie idealerweise komplett kennen. Richtig 
ist allerdings, daß man nicht in jedem OO-Programm alle Mittel und 
Möglichkeiten nutzen muß, die sie einem bietet. Ich persönlich schätze, 
daß etwa 70% meiner Python-Programme eigene Klassen nutzen, aber maximal 
5% meiner Python-Programme nutzen zB. die Vererbung. Es ist gut seinen 
ganzen Werkzeugkasten zu kennen, aber trotzdem nur den sinnvollen Teil 
davon zu benutzen -- nur weil ich einen Hammer darin habe, muß ich ihn 
nicht benutzen, um eine Schraube in die Wand zu bekommen.

[1] http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay_oop_en

von Wühlhase (Gast)


Lesenswert?

Tim schrieb:
> Danke für die bisherigen Kommentare.
> Welches Buch/Tutorial mit praxisnahen Beispielen käme noch infrage?
> Geplante Anwendung --> umgesetzte Klassenstruktur

Was ich dir empfehlen würde: Befasse dich mal mit Entwurfsmustern.

Die Programmierbeispiele sind zwar in Java, aber das Buch ist didaktisch 
großartig:
https://www.amazon.de/Entwurfsmuster-von-Kopf-bis-Fu%C3%9F/dp/3955619869/ref=sr_1_3?__mk_de_DE=%C3%85M%C3%85%C5%BD%C3%95%C3%91&keywords=java+von+kopf+bis+fu%C3%9F&qid=1578822471&sr=8-3

Ich kenne Python nicht, aber wenn die Sprache halbwegs etwas taugt dann 
sollten die Beispiele daraus auch auf Python anwendbar sein.

von Wühlhase (Gast)


Lesenswert?

Ich denke, danach hast du eine recht brauchbare Vorstellung davon, 
welche Vorteile Klassen usw. bieten.
Auch über die klasischen Beispiele, die Gegenstände im Code nachbilden 
wollen, hinaus.

von Kalle Knöpfle (Gast)


Lesenswert?

Das hier liest du dir durch:
http://openbook.rheinwerk-verlag.de/oop/

Nicht zu lange aufhalten nur damit die mal nen Überblick bekommst,
dann gehst du an dein Projekt.

von P. S. (namnyef)


Lesenswert?

Bei OOP geht es um Grunde darum das mentale Modell, das ein Mensch von 
einem Vorgang hat, im Code abzubilden. Das erhöht die Verständlichkeit 
und Wartbarkeit von Code und am Ende auch die Benutzbarkeit durch den 
Anwender. Das ist im Grunde eine der Kernaufgaben eines 
Software-Entwicklers und die Grundlage von Objektorientierung: Das 
mentale Modell des Anwenders in der Software abzubilden.

Meist wird allerdings weniger objektorientiert, sondern eher 
"klassenorientiert" programmiert. Also für jeden Mist eine Klasse und 
zum Selbstzweck völlige Eskalation mit Vererbung, Polymorphie, Kapselung 
und so weiter. Mit dem Ergebnis, dass sich niemand mehr im Code 
auskennt. Und der Benutzer wird erst recht nicht verstehen was die 
Software macht, weil das Interface irgendwann später schnell-schnell 
darüber gestülpt wird.

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

P. S. schrieb:
> Bei OOP geht es um Grunde darum das mentale Modell, das ein Mensch von
> einem Vorgang hat, im Code abzubilden.

Wir haben doch schon besprochen, dass das nicht funktioniert (siehe 
Kreis-Ellipse-Problem). Und ist eine Funktion nicht ein besseres Modell 
für einen "Vorgang"?
Das ist keine Kritik an OOP, nur an dieser Vorstellung von OOP als 
"mentales Modell" der Wirklichkeit.

von tipp (Gast)


Lesenswert?

Wühlhase schrieb:
> Ich kenne Python nicht, aber wenn die Sprache halbwegs etwas taugt dann
> sollten die Beispiele daraus auch auf Python anwendbar sein.

Wenn die Sprache etwas taugt (also nicht Java), dann sollte man keine 
dieser Entwurfsmuster mehr brauchen.

von MaWin (Gast)


Lesenswert?

Dirk K. schrieb:
> Der TO steigt gerade in die OOP ein und du kommst
> hier mit einem der kompliziertesten Probleme der
> OOP

Nicht wirklich, sondern dem üblichen Problem: übermässige Klassenanzahl.

von Dirk K. (merciless)


Lesenswert?

MaWin schrieb:
> Nicht wirklich, sondern dem üblichen Problem: übermässige Klassenanzahl.
Nein, einfach nochmal lesen, worauf ich mich bezogen habe.
Da steht nichts von Klassenanzahl.

Jaa, auch Trollen will gelernt sein.

merciless

von MaWin (Gast)


Lesenswert?

Dirk K. schrieb:
> Da steht nichts von Klassenanzahl

Aber ein Problem, wie es durch übermässige Klassenanzahl hervorgerufen 
wird.

Das übliche Problem.

Es ist Unsinn, bei OOP versuchen zu wollen "die Realität" mit möglichst 
filigraner Klasseneinteilung erfassen zu wollen. Dann läuft man 
unweigerlich früher oder später in eine Sackgasse weil in der Realitat 
eben nicht alles logisch ist.

Man darf und sollte eine neue zusätzliche Klasse nur dann anlegen, wenn 
es aus programmtechnischen Gründen handfeste Argumente dafür gibt.

Viele Programme sind beispielsweise mit einer einzigen Klasse "die App" 
gut bedient. Das ist fast wie programmieren ohne Klassen, in dem alle 
sonst als global deklarierten Variablen nun Membervariablen werden und 
alle Funktionen zu Methoden dieser Klasse werden die natürlich auf alle 
Members zugreifen können. Man sollte diese Umwandlung eines bisher 
prozeduralen Programms machen (oder gleich so starten), wenn man im 
selben Kontext mehrere Instanzen davon laufen lassen will.
Es gibt dann also einen handfesten Grund: man kann nicht dieselbe 
globale Variable doppelt haben, und OOP ist hier die naheliegende und 
übersichtlich strukturierte Lösung.

Ebenso bei weiteren Schritten: ich will Fehlerbehandlung durch 
Exceptions haben, dann darf ich nicht einfach aus einer Funktion 
herauspoltern die dynamisch Speicher allokiert hat, sondern ich muss 
diese Speicherallokation in eine Klasse verpacken deren Destruktor den 
Speicher wieder frei gibt. Auch hier gibt es einen handfesten Grund 
warum man eine Klasse (oder hunderte..) einführt.

etc. Man hinterfrage bei jeder Klasse, ob es wirklich aus der 
Programmierrealität heraus einen handfesten Grund gibt, sie einzuführen.

Das schlimmste was man tun kann ist beispielsweise, für jedes 
Datenbankfeld "Anrede", "Vorname", "Nachname", Strasse" ... eine eigene 
Klasse einzuführen. Da verbaut man sich jede genetische Verarbeitung.

Leider führen viele OOP Kurse genau zu der Lösung, Klassen als 
Selbstzweck anzulegen.

von Wühlhase (Gast)


Lesenswert?

tipp schrieb:
> Wenn die Sprache etwas taugt (also nicht Java), dann sollte man keine
> dieser Entwurfsmuster mehr brauchen.

Aha...warum sollte Java denn nichts taugen?
Und wie sollte eine Sprache aussehen, die zwar objektorientiert ist, 
aber in der keine Entwurfsmuster gebraucht werden? Was wäre in so einer 
Sprache denn anders? Da bin ich doch wirklich mal gespannt...

Entwurfsmuster braucht man generell nicht. Aber sie machen einem das 
Leben erheblich einfacher. Unabhängig von der Programmiersprache.


MaWin schrieb:
> Nicht wirklich, sondern dem üblichen Problem: übermässige Klassenanzahl.

Was für ein Unsinn...du hast den Grund, weshalb man überhaupt in Klassen 
aufteilt, absolut gar nicht verstanden. Ich gehe mal exemplarisch nur 
auf eine Perle ein:


MaWin schrieb:
> Man darf und sollte eine neue zusätzliche Klasse nur dann anlegen, wenn
> es aus programmtechnischen Gründen handfeste Argumente dafür gibt.

Klassen hat man, um einen definierten Kontext zu schaffen. Absolut 
unabhängig vom Rest des Programms.
Eine Abstraktionsebene schaffen zu wollen (z.B. weil man eine 
Fremdbibliothek verwenden will und noch nicht klar ist, ob man die 
später evt. durch eine andere ersetzen möchte) oder einfach einen 
Funktionsteil abzugrenzen. Wenn du z.B. eine Eigenschaft eines (anderen) 
Objekts hast, das du als Integer behandeln willst aber den Wertebereich 
begrenzen willst.

Solche Banalitäten sind Grund genug, eine neue Klasse aufzumachen.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

MaWin schrieb:
> Dirk K. schrieb:
>> Der TO steigt gerade in die OOP ein und du kommst
>> hier mit einem der kompliziertesten Probleme der
>> OOP
>
> Nicht wirklich, sondern dem üblichen Problem: übermässige Klassenanzahl.

Es kann gute gründe geben, für Kreise und Ellipsen andere Klassen zu 
benötigen. Das Problem liegt hier nicht bei der Anzahl Klassen, sondern 
bei der unnötigen/falschen Vererbung. Solange man diese hier nur nicht 
direkt zwischen Kreisen und Ellipsen macht, hat man das Problem nicht 
mehr.

Aber auch sonst ist die Klassenanzahl eigentlich nie das Hauptproblem. 
Um noch ein anderes Beispiel zu bringen, eine Person hat eine Adresse. 
Es wird nicht schaden, die Adresse in eine extra Klasse auszulagern. Die 
Person sollte nur weiterhin nicht von Adresse erben. Ich kann da 
beliebig mehr Eigenschaften zusammen in Klassen auslagern, es mag 
weniger übersichtlich werden, aber es kann nicht so grundlegend falsch 
werden, wie das mit dem Einführen einer neuen Vererbung möglich wäre.

von Jemand (Gast)


Lesenswert?

🐧 DPA 🐧 schrieb:
> Aber auch sonst ist die Klassenanzahl eigentlich nie das Hauptproblem.
> Um noch ein anderes Beispiel zu bringen, eine Person hat eine Adresse.
> Es wird nicht schaden, die Adresse in eine extra Klasse auszulagern. Die
> Person sollte nur weiterhin nicht von Adresse erben. Ich kann da
> beliebig mehr Eigenschaften zusammen in Klassen auslagern, es mag
> weniger übersichtlich werden, aber es kann nicht so grundlegend falsch
> werden, wie das mit dem Einführen einer neuen Vererbung möglich wäre.

Jetzt hör' doch bitte mal mit Deinem Kreuzzug gegen die Vererbung auf. 
Kein denkender Entwickler der Welt käme auf die irre Idee, eine 
Personenklasse von einer Adressenklasse erben zu lassen. Reale Personen 
haben nämlich bisweilen mehrere Adressen, anders gesagt: die 
Personenklasse beinhaltet eine Liste von Adressen. So würde man das ja 
auch in einer Datenbank modellieren, als 1:n.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

Jemand schrieb:
> Kein denkender Entwickler der Welt käme auf die irre Idee, eine
> Personenklasse von einer Adressenklasse erben zu lassen.

https://github.com/search?q=%22Person+extends+Address%22&type=code

von Oliver S. (oliverso)


Lesenswert?

Je nun, in den unendlichen Weiten des Netzes findet sich für jeden noch 
so abstrusen Blödsinn immer ein Beispiel.

Oliver

: Bearbeitet durch User
von 🐧 DPA 🐧 (Gast)


Lesenswert?

Es sind dennoch echte Entwickler, die den Fehler machen. Ausserdem ist 
es ein Fehler, welcher Anfängern gerne unterläuft.

von Dirk K. (merciless)


Lesenswert?

🐧 DPA 🐧 schrieb:
> Es sind dennoch echte Entwickler, die den Fehler machen. Ausserdem ist
> es ein Fehler, welcher Anfängern gerne unterläuft.
Köche schneiden sich im Laufe ihres Lebens öfter in die
Finger als andere Menschen, außerdem passiert das jungen
Köchen häufiger: Lasst uns Messer verbieten und die
Schweinekeule im ganzen über das Feuer hängen, dann
kann dies nicht mehr passieren.

merciless

von 🐧 DPA 🐧 (Gast)


Lesenswert?

Es ist sinvoll, jemanden, der zum ersten mal Kocht darauf Hinzuweisen, 
dass das Messer scharf ist. Ausserdem gibt man Kleinkindern bewusst noch 
keine Messer in die Hand, sondern erst denjenigen, die Messer bereits 
verstehen und vermutlich damit umgehen werden können.

von P. S. (namnyef)


Lesenswert?

mh schrieb:
> Wir haben doch schon besprochen, dass das nicht funktioniert (siehe
> Kreis-Ellipse-Problem).

Ja. Wenn man sich auf mathematische/theoretische Spitzfindigkeiten statt 
auf die eigentliche Anwendung und den Benutzer konzentriert, kann man 
durchaus die Meinung vertreten, "dass das nicht funktioniert".

von mh (Gast)


Lesenswert?

mh schrieb:
> P. S. schrieb:
>> Bei OOP geht es um Grunde darum das mentale Modell, das ein Mensch von
>> einem Vorgang hat, im Code abzubilden.

P. S. schrieb:
> mh schrieb:
>> Wir haben doch schon besprochen, dass das nicht funktioniert (siehe
>> Kreis-Ellipse-Problem).
>
> Ja. Wenn man sich auf mathematische/theoretische Spitzfindigkeiten statt
> auf die eigentliche Anwendung und den Benutzer konzentriert, kann man
> durchaus die Meinung vertreten, "dass das nicht funktioniert".

Ich kann nichts dafür, dass sich mein "mentales Modell" im Allgemeinen 
gut mit der Mathematik verträgt.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

Das Kreis-Ellipse Problem hat man immer, wenn die abgeleitete Klasse 
einschränkungen, eine fehlende Aktion oder Eigenschaft gegenüber der 
Basisklasse hat. Mit Mathematik hat das nichts zutun. Ist ein Pinguin 
ein Vogel?
1
for vogel in voegel:
2
  vogel.takeoff()

von Matthias S. (da_user)


Lesenswert?

Pinguine fliegen aber! Halt in einem anderen Medium.

mh schrieb:
> Ich kann nichts dafür, dass sich mein "mentales Modell" im Allgemeinen
> gut mit der Mathematik verträgt.

Mag gerne sein.
Man kann mit solch einer theoretischen Frage die Praxis auch 
verkomplizieren. Nicht das es nicht wertvoll wäre das 
Kreis-/Ellipse-Problem zu kennen, in der Praxis wird einem dieses 
Problem aber wohl er nicht so oft begegnen und wenn, wird man es - der 
Anwendung gerecht - umschiffen können.
OOP ist ein Konzept, um vieles besser verständlich, einfacher zu 
implementieren,... usw. usf. zu machen. Man kann das Konzept aber auch 
versuchen mit aller Gewalt jedem Problem aufzusetzen, feststellen dass 
das nicht immer so gut funktioniert und dann das ganze Konzept doof 
finden...

von Dirk K. (merciless)


Lesenswert?

🐧 DPA 🐧 schrieb:
> Das Kreis-Ellipse Problem hat man immer, wenn die abgeleitete Klasse
> einschränkungen, eine fehlende Aktion oder Eigenschaft gegenüber der
> Basisklasse hat. Mit Mathematik hat das nichts zutun. Ist ein Pinguin
> ein Vogel?
>
1
> for vogel in voegel:
2
>   vogel.takeoff()
3
>

Die Annahme, dass alle Vögel können fliegen,
ist falsch (und das weisst du genau).

https://de.wikipedia.org/wiki/V%C3%B6gel

merciless

von Egon D. (Gast)


Lesenswert?

Matthias S. schrieb:

> Pinguine fliegen aber! Halt in einem anderen Medium.

Und "wahr" ist "falsch"! Nur halt in negativer Logik.


> OOP ist ein Konzept, um vieles besser verständlich,
> einfacher zu implementieren,... usw. usf. zu machen.

Der Kritikpunkt war aber nicht OOP allgemein, sondern
die VERERBUNG.

von Egon D. (Gast)


Lesenswert?

Dirk K. schrieb:

> Die Annahme, dass alle Vögel können fliegen,
> ist falsch (und das weisst du genau).

Kann es sein, dass Du Daniels Beispiel nicht
verstanden hast?

von Matthias S. (da_user)


Lesenswert?

Egon D. schrieb:
> Matthias S. schrieb:
>
>> Pinguine fliegen aber! Halt in einem anderen Medium.
>
> Und "wahr" ist "falsch"! Nur halt in negativer Logik.

Was aber nichts daran ändert, dass der Pinguinexperte von einem Flug 
spricht. Was kommt jetzt rein in das Programm? Das was der 
Pinguinexperte oder das was der Programmierexperte sagt?

Egon D. schrieb:
> Der Kritikpunkt war aber nicht OOP allgemein, sondern
> die VERERBUNG.

Dann ersetze halt OOP mit "Vererbung".

von Dirk K. (merciless)


Lesenswert?

Egon D. schrieb:
> Kann es sein, dass Du Daniels Beispiel nicht
> verstanden hast?
Nein, was ich schon am 09.09. kritisiert habe:
Daniel konfrontiert einen Einsteiger in Sachen OOP
mit einem der kompliziertesten Design-Probleme in
der OOP. Und der Einsteiger wird nicht verstehen
können, um was es da geht. Ich sage: Lauf mal los
und designe Klassen, wie du meinst. Irgendwann wird
er eine Basisklasse Vogel mit einer Methode fliege()
haben und den Pinguin implementieren wollen:
DANN wird er erkennen, dass es ein Problem ist und
genau DANN kann man darüber diskutieren.

Ich zähle mich zu den Puristen: Ableitungen nur von
Interfaces und abstrakten Basisklassen (letzteres
sehr selten) - Exception-Klassen sind eine Ausnahme.

https://en.wikipedia.org/wiki/Composition_over_inheritance

merciless

: Bearbeitet durch User
von Egon D. (Gast)


Lesenswert?

Dirk K. schrieb:

> Egon D. schrieb:
>> Kann es sein, dass Du Daniels Beispiel nicht
>> verstanden hast?
>
> Nein, was ich schon am 09.09. kritisiert habe:
> Daniel konfrontiert einen Einsteiger in Sachen OOP
> mit einem der kompliziertesten Design-Probleme in
> der OOP.

Nein, überhaupt nicht.

Daniel versucht nur daraufhinzuweisen, dass dieses
angeblich so komplizierte OOP-Problem nur eine
zwangsläufige, aber abstruse Folgerung aus einer
(m.E.) völlig abseitigen Auffassung von OOP ist.

Wer in Automatentheorie beschlagen ist, kann sich
leicht überlegen, dass "Objekte" (im Sinne der OOP)
einfach Automaten sind; die zur Programmlaufzeit
aufgebaute Objekthierarchie ist also einfach als
Automatennetz anzusehen.

Vererbung ist für das Grundverständnis dieses
Sachverhaltens erstmal nicht erforderlich.
Vererbung mag eine nützliche Idee sein, das will
ich nicht beurteilen, aber sie gehört ganz sicher
nicht zum logischen Kern der OOP.


> Und der Einsteiger wird nicht verstehen können,
> um was es da geht.

Das ist aber nicht schuld des Einsteigers, sondern
einer völlig verqueren Auffassung von OOP. :)


> Ich sage: Lauf mal los und designe Klassen, wie
> du meinst. Irgendwann wird er eine Basisklasse
> Vogel mit einer Methode fliege() haben und den
> Pinguin implementieren wollen: DANN wird er
> erkennen, dass es ein Problem ist und genau
> DANN kann man darüber diskutieren.

Naja, in der Datenbanktheorie hat man relativ
frühzeitig verstanden, dass Hierarchien bzw.
Bäume nicht das alleinseligmachende Rezept für
die Strukturierung von Dingen und ihren
Eigenschaften ist, und hat die Normalformen-
theorie entwickelt.

Insofern wundert mich nicht wirklich, dass sich eine
Vererbungs- HIERARCHIE nicht als praxisgerecht erweist.
Genau DAS ist m.E. auch der Kern von Daniels Kritik:
Dass nämlich die Vererbung nicht als technisches
Hilfsmittel dargestellt wird, sondern als einfacher
und offensichtlicher Weg, die Verhältnisse des realen
Lebens im Computer abzubilden -- und genau letzteres
stimmt halt nicht.

von A. S. (Gast)


Lesenswert?

Egon D. schrieb:
> Insofern wundert mich nicht wirklich, dass sich eine Vererbungs-
> HIERARCHIE nicht als praxisgerecht erweist. Genau DAS ist m.E. auch der
> Kern von Daniels Kritik:

Vielleicht würde man heute statt von Vererbung eher von Teilen oder 
Plagiat sprechen.

Kein "ist ein" sondern eher "kann auch" bzw. "hat auch".

Die ganze "Uhu ist ein Vogel" Metapher scheitert einfach daran, dass es 
die platonische "Idee" eines Vogels nicht gibt, auch nicht die 
Unterscheidung in akzidenz und Essenz.

Ein "Vogel" ist einfach nur ein willkürliches subset der realen 
Ausprägungen aller Vögel.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

@Egon D. (egon_d)

Du interpretierst da etwas zu viel in meine bisherigen Aussegan hinein.

von Jemand (Gast)


Lesenswert?

🐧 DPA 🐧 schrieb:
> Das Kreis-Ellipse Problem hat man immer, wenn die abgeleitete Klasse
> einschränkungen, eine fehlende Aktion oder Eigenschaft gegenüber der
> Basisklasse hat. Mit Mathematik hat das nichts zutun. Ist ein Pinguin
> ein Vogel?
>
1
> for vogel in voegel:
2
>   vogel.takeoff()
3
>

Ich fürchte, der ganze Denkansatz ist ein bisschen... zu kurz gedacht, 
und dies gilt sowohl für das Beispiel mit den Pinguinen, als auch für 
das Kreis-Ellipsendings. Natürlich, für den Biologen ist der Pinguin ein 
Vogel und für den Mathematiker der Kreis eine Sonderform der Ellipse.

Es gibt aber keinen Grund der Welt, warum ein Entwickler oder 
Softwaredesigner sich daran orientieren, also ein Pingu von BaseVogel 
oder der Kreis von der Ellipse erben muß. Ein kluger Entwickler würde 
sowas entweder beim Erstentwurf oder spätestens beim Refactoring anders 
modellieren, für den Vogel also:
1
BaseVogel
2
 |  |
3
 |  +- LaufVogel
4
 |      +- Pinguin
5
 |      +- Strauß
6
 |      +- Emu
7
 |      +- Nandu
8
 |      +- Kiwi
9
 |
10
 +- FlugVogel
11
     +- Amsel
12
     +- Drossel
13
     +- Fink
14
     +- Star

Der Grundfehler bei Deinem Vögelbeispiel liegt darin, anzunehmen, daß 
alle Vögel fliegen können. Genau das ist aber nicht der Fall, wie Du 
meinem Modell entnehmen kannst.

Nebenbei: in Programmiersprachen, die ihre Entwicklergemeinde nicht für 
zu dämlitsch dafür halten, gibt es natürlich auch noch die Möglichkeiten 
der Mehrfachvererbung. Damit könnte man einen Vogel zum Beispiel einfach 
als nicht schwimm-, tauch- und flugfähigen Vogel definieren, und die 
Flug-, Schwimm- und Tauchfähigkeiten als weitere Klassen. So würde unser 
Pinguin von BaseVogel, SchwimmMixin und TauchMixin erben, unsere Ente 
dagegen von BaseVogel, Flug- und SchwimmMixin, und die Amsel von 
BaseVogel und FlugMixin. Alles absolut sauber modelliert, und je nach 
Anforderungen (und ggf. globaler oder lokaler Konfiguration) kann ein 
Aufruf der Methode takeoff() für nicht flugfähiges Geflügel entweder 
eine Exception werfen, oder die Eigenschaft Höhe auf einen Wert != 0 
setzen und eine 0 zurückgeben, oder... ja, ganz genau.

Viel wichtiger, als OO-Einsteiger vor den Gefahren und Fallstricken der 
OO zu warnen, ist es daher IMHO, ihnen zu raten, keine Angst vor einem 
Refactoring zu haben, wenn die gewählte Architektur sich als suboptimal 
und nicht zu den Anforderungen passend entpuppen sollte. Es kostet 
nämlich auf mittlere und lange Sicht ein Vielfaches, um ein verkorkstes 
Design herumzuentwickeln, als seine ursprünglichen Designfehler 
beizubehalten.

von Daniel A. (daniel-a)


Lesenswert?

@Jemand

Genau diese Überlegungen sind es, die das Beispiel hervorrufen sollte. 
Bravo!

von Dirk K. (merciless)


Lesenswert?

Da wir hier jetzt ins akademische Abdriften:
So in etwa wäre mein erster Entwurf. So etwas wie
die abstrakte Basisklasse Bird würde ich auch nur
dann einbauen, wenn das technisch notwendig sein
sollte. Ich gehe hier auch davon aus, dass es
nicht unbedingt wichtig ist, auf welchem Tier
Methoden aufgerufen werden, sondern dass immer
auf den Interfaces operiert wird. An der Klasse
Tuna kann man auch erkennen, dass es Methoden gibt,
die für Vögel und Fische Sinn machen können.
1
interface IEggLayingAnimal
2
{
3
  void LayEgg();
4
}
5
6
interface IFlyingAnimal
7
{
8
  void Fly();
9
}
10
11
interface ISwimmingAnimal
12
{
13
  void Swim();
14
}
15
16
abstract class Bird
17
{
18
  private Color FeatherColor;
19
}
20
21
class Penguin : Bird, IEggLayingAnimal, ISwimmingAnimal
22
{
23
}
24
25
class Owl : Bird, IEggLayingAnimal, IFlyingAnimal
26
{
27
}
28
29
class Tuna : IEggLayingAnimal, ISwimmingAnimal
30
{
31
}

merciless

von A. S. (Gast)


Lesenswert?

Jemand schrieb:
> Ein kluger Entwickler würde
> sowas entweder beim Erstentwurf oder spätestens beim Refactoring anders
> modellieren, für den Vogel also:

Ja, kann man machen. Das teilt die Vögel in flugfähige und -unfähige. 
Nur leider gibt es dutzende "Teilungen", die genauso Sinn machen und 
quer durch diese Teilung laufen. Tag-Nacht, Tier- oder Planzenfresser, 
Laufen oder Hüpfen, bauen Nester oder nicht … und das alles mit einem 
Graubereich dazwischen.

Das ist der Grund, warum das nur in akademisch einfachen Beispielen 
funktioniert.

In der Praxis brauchst Du dann doch eine Überklasse Vögel (alle 
"fliegen", wenn auch manche nur vor die Füße) oder nur eine Basisklasse 
(2 Beine, legen Eier, aber können weder fliegen noch laufen oder hüpfen)

von Matthias S. (da_user)


Lesenswert?

Man kann aber auch einfach gucken, was man braucht.
Wenn man bei den Vögeln z.B. "fliegen" nicht braucht, sondern nur "lege 
Ei", warum sollte ich mir dann den Stress um das "fliegen" machen? Weil 
das in ferner Zukunft, evtl., u.U., vllt., irgendwann einmal, gebraucht 
werden könnte?

von 🐧 DPA 🐧 (Gast)


Lesenswert?

Nunja, oft hat man die umgekehrte Situation. Man hat viele Flugfähige 
Vögel, und irgendwo ist Logik, die bei Vögeln die Flugfunktion aufruft.
Wenn dann alles schon fertig ist kommt jemand, und will noch einen 
Pinguin, einen Dodo und einen Kiwi haben...

von Dirk K. (merciless)


Lesenswert?

🐧 DPA 🐧 schrieb:
> Nunja, oft hat man die umgekehrte Situation. Man hat viele Flugfähige
> Vögel, und irgendwo ist Logik, die bei Vögeln die Flugfunktion aufruft.
> Wenn dann alles schon fertig ist kommt jemand, und will noch einen
> Pinguin, einen Dodo und einen Kiwi haben...
und eine Fledermaus...

merciless

von Egon D. (Gast)


Lesenswert?

Dirk K. schrieb:

> 🐧 DPA 🐧 schrieb:
>> Nunja, oft hat man die umgekehrte Situation. Man hat
>> viele Flugfähige Vögel, und irgendwo ist Logik, die
>> bei Vögeln die Flugfunktion aufruft. Wenn dann alles
>> schon fertig ist kommt jemand, und will noch einen
>> Pinguin, einen Dodo und einen Kiwi haben...
>
> und eine Fledermaus...

... und dann noch eine Fliege, eine Hornisse, einen
Lenkdrachen, ein Luftschiff und einen Jagdflieger?

Langsam wird es albern.

von Egon D. (Gast)


Lesenswert?

Matthias S. schrieb:

> Man kann aber auch einfach gucken, was man
> braucht.

Ähh... ja?!


> Wenn man bei den Vögeln z.B. "fliegen" nicht braucht,
> sondern nur "lege Ei", warum sollte ich mir dann den
> Stress um das "fliegen" machen?

Musst Du nicht. Verlangt niemand.


> Weil das in ferner Zukunft, evtl., u.U., vllt.,
> irgendwann einmal, gebraucht werden könnte?

Nee. YAGNI.

Die Diskussion hat sich nur daran entzündet, dass oben
jemand -- wie das in alten schlechten Büchern üblich
war -- Vererbung als einfaches und offensichtliches Mittel
zum Abbilden der Realität im Computer angepriesen hat.

Einfache Vererbung führt aber zu einer Hierarchie, und
von den Datenbanken her ist bekannt, dass die reale Welt
nur selten als Hierarchie abgebildet werden kann.

Der Pragmatiker tut genau das, was Du vorgeschlagen hast:
Er modelliert zunächst im Geiste den Teil der Realität,
der für ihn relevant ist, und überträgt diesen anschließend
in Programmform in den Rechner.

von A. S. (Gast)


Lesenswert?

Egon D. schrieb:
> Einfache Vererbung führt aber zu einer Hierarchie, und von den
> Datenbanken her ist bekannt, dass die reale Welt nur selten als
> Hierarchie abgebildet werden kann.

Kannst Du das bitte noch einmal in fett setzen und am besten auch noch 
in die entsprechenden Kapitel hier einfügen? Treffend formuliert.

von Dirk K. (merciless)


Lesenswert?

Egon D. schrieb:
> ... und dann noch eine Fliege, eine Hornisse, einen
> Lenkdrachen, ein Luftschiff und einen Jagdflieger?
>
> Langsam wird es albern.
Nee eben nicht. Genau das ist die Realität.
Deswegen operiere ich nur auf einem Interface,
dass die Methode Fly() bietet. Dann bin ich
flexibel, egal was da verarbeitet werden soll.

Vererbung hat ihre Berechtigung, sollte aber
sehr umsichtig eingesetzt werden. Meistens gibt
es bessere Lösungen. Aber das lernt man nicht
in "OOP für Dummies in 3 Tagen".

merciless

von Jemand (Gast)


Lesenswert?

A. S. schrieb:
> Egon D. schrieb:
>> Einfache Vererbung führt aber zu einer Hierarchie, und von den
>> Datenbanken her ist bekannt, dass die reale Welt nur selten als
>> Hierarchie abgebildet werden kann.
>
> Kannst Du das bitte noch einmal in fett setzen und am besten auch noch
> in die entsprechenden Kapitel hier einfügen? Treffend formuliert.

Richtig ist: so etwas kann mit relationalen Datenbanken nicht sonderlich 
gut abgebildet werden, wobei auch das primär für relationale Datenbanken 
gilt. Andere Datenbanken sind entweder ohnehin hierarchisch aufgebaut, 
man denke da nur an Verzeichnisdienste wie OpenLDAP oder 
ActiveDirectory, an Spezialisten wie IBMs Information Management System 
oder auch die Windows Registry, an neuere NoSQL-Datenbanktypen wie 
MongoDB oder RestDB oder an Grafendatenbanken wie OrientDB oder Neo4j. 
Allerdings ist es in relationalen Datenbanken zwar ein wenig aufwändiger 
und manchmal nicht besonders elegant, aber auch dort kann man mit 
hierarchischen Datenmodellen arbeiten -- allein Joey Celko hat viele 
spannende Dinge zu Bäumen und Hierarchien in klassischen relationalen 
Datenbanken geschrieben.

Fakt ist aber auch, daß nichts so einfach ist, wie es scheint: die 
meisten Menschen würden es auf den ersten Blick als die wohl wichtigste 
gemeinsame Eigenschaft von Vögeln bezeichnen, daß sie fliegen können. 
Trotzdem sind Dodos und Pinguine zwar Vögel, aber nicht flugfähig. Und 
jetzt? Sind Pinguine und Dodos jetzt keine Vögel mehr, sondern nur noch 
Tiere? Oder ist vielleicht nur die zugrundeliegende Annahme falsch, daß 
alle Vögel fliegen können?

Nun, am Ende hängt es immer vom konkreten Anwendungsfall ab, ob 
Vererbung oder Komposition benutzt wird, welche Eigenschaften und 
Methoden implementiert werden, und so weiter. Sich hier ohne Kenntnis 
des Anwendungsfalls einfach mal festzulegen und die Vererbung zu einem 
Antipattern zu erklären, ist genauso zu kurz gesprungen wie sie zum 
Allheilmittel hochzujubeln.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.