Grad zufaellig entdeckt: Object-Oriented Programming is Bad - YouTube
Ziemlich radikal zwar, aber dennoch interessante Sichtweise. Glaub, da muss ich erstmal drueber nachdenken
Grad zufaellig entdeckt: Object-Oriented Programming is Bad - YouTube
Ziemlich radikal zwar, aber dennoch interessante Sichtweise. Glaub, da muss ich erstmal drueber nachdenken
OK, habâs mir mal reingezogen und ich muss sagen, es war sehr gut fĂŒr meine Fitness weil ich andauernd den Kopf schĂŒtteln musste.
Sein wesentlicher Kritikpunkt ist ein (aus meiner Sicht) falsches VerstĂ€ndnis von âEncapsulationâ: Er behauptet, dass ein Objekt nicht nur fĂŒr seinen eigenen State verantwortlich ist, sondern auch fĂŒr den seiner AbhĂ€ngigkeiten, und dass es eine Verletzung der âEncapsulationâ ist, wenn andere Objekte den State seiner AbhĂ€ngigkeiten Ă€ndern. Auch die These, das die Weitergabe von Objekten als Methodenparameter nicht dem âNachrichtenparadigmaâ entspricht ist IMHO ziehmich weit her geholt. Und mehr hat er ja gar nicht gegen OOP.
Und das eine Klasse mit Namen X nicht die FunktionalitÀt X (komplett) enthÀlt ist IMHO auch nicht ein Problem von OOP, sondern von schlechten Programmierern.
Richtig absurd wirdâs dann ab 40:00, wo er dann seine GegenvorschlĂ€ge bringt.
Erst erzĂ€hlt er, dass âcomposed methodsâ blöd sind, weil es schwer ist, gute Methodennamen zu finden, und man doch lieber lange Methoden mit âAbschnittskommentarenâ schreiben sollte (Wahrscheinlich ist er der einzige Programmierer Weltweit, der diese Kommentare korrekt pflegt, wenn sich was Ă€ndertâŠ), um dann nach einem neuen Feature zu rufen, dass ihm âprivate scopeâ innerhalb der langen Methode ermöglicht, also eine Lösung fĂŒr ein Problem, dass er mit âcomposed methodsâ nicht hĂ€tteâŠ
Also nein, mich hat er nicht ĂŒberzeugt.
bye
TT
Naja, das is doch im Kern einfach nur eine Argumentation fuer functional programming - also es ist egal, ob du nun ein internes Feld direkt aendern kannst (z.b. wenn es pubic waere) oder ob du einen Setter dazu aufrufen musst; entscheidend ist, DASS du den internen Zustand eines Objektes aendern kannst. In der funktionalen Welt geht das einfach nicht. Da legt man eine Kopie mit genau dieser Aenderung an.
Bei der Kommentargeschichte geh ich mit, das is ziemlich wirr.
Gescopte Methoden (bzw functions) waeren trotzdem nett, leider ist hier auch Java 8 ziemlich eingeschraenkt (uebrigens koennte er sich diese use Sache mit JavaScript (was er ja da verwendet) relativ easy nachbauen).
Beim Thema Vererbung bin ich mir auch nich so ganz sicher. Jedenfalls achte ich inzwischen schon darauf, moeglichst kleine oder besser noch gar keine Hierarchien zu benutzen und eher mit Interfaces zu arbeiten. Ich hab einfach schon Vererbungshierarchien erleben duerfen, da will ich lieber nicht drueber reden
[quote=schalentier]Naja, das is doch im Kern einfach nur eine Argumentation fuer functional programming - also es ist egal, ob du nun ein internes Feld direkt aendern kannst (z.b. wenn es pubic waere) oder ob du einen Setter dazu aufrufen musst; [/quote]Diese Aussage geht leider am Wesen der OOP vorbei: Den State ĂŒber Setter zu Ă€ndern ist nicht OOP.
Aber ich will mal nicht ErbsenzÀhlen, denn es geht Dir wohl um die ZustandsÀnderung des Objekts selbst.
Das ist in der OO-Welt aber OK, denn Objekte haben nun mal einen State, der ĂŒber Nachrichten verĂ€ndert werden kann.
[quote=schalentier;131358]entscheidend ist, DASS du den internen Zustand eines Objektes aendern kannst. In der funktionalen Welt geht das einfach nicht. Da legt man eine Kopie mit genau dieser Aenderung an.[/quote]FĂŒr mich ist die Frage, ob das immer sinnvoll ist, noch nicht beantwortet. Auch in dem Video ist ja das Problem angesprochen worden, dass eine Anwendung irgendwie einen State verwalten muss. Sein Vorschlag war globale Variablen einzusetzen. âBack to the rootsâ so zu sagen⊠;o)
[quote=schalentier;131358]Beim Thema Vererbung bin ich mir auch nich so ganz sicher.[/quote]Das Video hat Vererbung gar nicht so im Fokus gehabt.
Aus meiner Sicht gibt es 2 Probleme: Equals-Implementierungen und Testbarkeit.
Daher bilde ich Vererbung ĂŒber Interfaces ab und bei den Implementierungen halte ich mich an das âFavour Composition over Inheritanceâ Pattern.
Aber solche Design-Pattern sind ja laut Video nur Pflaster fĂŒr die ach so kaputte OO-WeltâŠ
BTW: Falls ich das Video richtig verstanden habe scheint er ja TDD an sich ganz OK zu finden. Da möchte ich doch mal sehen, wie mal fĂŒr funktionale Programmteile Unittests so schreibt, dass die die RTFM-Bedingungen erfĂŒllen, und wie das dann mit âprivate scoped inline functionsâ harmoniertâŠ
bye
TT
Ja, habâs auch mal angeschaut, und stimme einigen der oben schon genannten Punkte zu. Wo er da ĂŒber âEncapsulationâ redet bin ich dann gedanklich kurz abgedriftet, weil es einfach ZU abstrus erschien (z.B. das was er ĂŒber die âZustĂ€ndigkeiten fĂŒr den Stateâ sagt usw). Nur auf der höchsten Abstaktionsebene (sic!) kann ich einigem davon zustimmen: Wenn man alles immer bis zum Anschlag Schulbuch-OO-mĂ€Ăig durchplanen will, ist es schwer, ein Ende zu finden, an dem nicht jemand sagt: âDu könntest da aber noch diesen Teil in einen X-er auslagernâ.
Ingesamt ist da viel Kritik hinter einem provokativen Titel versteckt. (âPapers whose title contains âconsidered harmfulâ are considered harmfulâ), aber finde auch, dass einiges der Kritik auf ein âfalschesâ (oder etwas verzerrtes/ĂŒberzogenes) VerstĂ€ndnis hindeutet.
Wieder recht abstrakt: Der Schönheit von âFunktionen (als Building Blocks)â stimme ich zu, mit der Konsequenz dass ich mir schon gelegentlich von durch andere Paradigmen indoktrinierten Rosinenproduzenten anhören durfte: âIeeeh, static methods, die sind schlecht, die kann man nicht mocken!!!11einsâ (Ja. Und? Das ist egal!).
Das Ende war dann aber wieder etwas wirr: Nicht in Funktionen auslagern. Also dann doch. Aber wieder nicht. Oder doch, aber nur speziell gescopt. Mit super-spezial-Funktionen, die es in keiner Programmiersprache gibt. (AuĂer in C++ - mit lambda captures, die GENAU seinem âuseâ entsprechenâŠ).
Tja, wie auch immer: ScheiĂcode kann man in jeder Sprache und mit jedem Paradigma schreiben
[quote=Marco13]finde auch, dass einiges der Kritik auf ein âfalschesâ (oder etwas verzerrtes/ĂŒberzogenes) VerstĂ€ndnis hindeutet.[/quote]Genau mein Eindruck. Er sagt zwar, dass er schon lange (OO-) programmiert, aber man kann eine Sache auch lange falsch machen⊠;o)
[quote=Marco13;131368]âIeeeh, static methods, die sind schlecht, die kann man nicht mocken!!!11einsâ (Ja. Und? Das ist egal!).[/quote]Nur solange du keine echten (im Sinne von RTFM) Unittests schreibst.
[edit SlaterB: weiter dazu abgespalten hier: https://forum.byte-welt.net/java-forum/allgemeine-themen/19089-test-mocken-statischer-methoden.html ]
[quote=Marco13;131368]Tja, wie auch immer: ScheiĂcode kann man in jeder Sprache und mit jedem Paradigma schreiben[/quote]Da hast Du sowas von RechtâŠ
bye
TT
Ich versteh eigentlich nicht, was wirklich allgemein gegen objektorientiertes Programmieren spricht. SchlieĂlich ist es ein Paradigma, welches die Eleganz unserer deutschen/englischen Sprache bietet. Was jedoch Java aus OOP gemacht, finde ich gruselig, wie etwa AbstractNamingConventionProxyFactory. In C++ oder Python ist wenigstens keine Funktion gezwungen sich in einer Klasse zu befinden, was dann eben nicht zu solchen Conventions fĂŒhrt.
Die Aussage, dass es ~ânicht schön ist, dass in Java alles in einer Klasse stehen muss, und es keine âfreienâ Funktionen gibtâ, hat mich nie ganz ĂŒberzeugt. Solche Funktionen gibt es in C++ âpraktischâ auch nicht - und wer glaubt, dass es sie DOCH gibt, der möge sich in Java eine Klasse definieren, die âstdâ heiĂt, und nur statische Methoden enthĂ€lt, damit er weiterhin std::max
(bzw. dann eben std.max
) aufrufen kann.
Das Argument, dass OOP âdie Eleganz unserer ⊠Sprache bietetâ stimmt einerseits. Aber das kritisiert bzw. hinterfragt er ja auch - und IMHO, auch zu Recht. Es ist zwar einerseits schön und erstrebenswert, wenn sich Code wie âProsaâ liest. Aber oft stellen sich dann Fragen zur Modellierung (die mich, zugegeben, manchmal zur Verzweiflung bringen). Sein Beispiel war ja grob (hier leicht erweitert), welches hiervon die ârichtigeâ Modellierung ist:
message.send();
sender.send(message);
sender.send(message, receiver);
receiver.receive(message);
receiver.receive(message, sender);
transferrer.transfer(message, sender, receiver);
MessageTransfers.create(message, sender, receiver).exeucte();
Das sind zumindest die Fragen, die mich Nachts nicht schlafen lassen, weil alles Vor- und Nachteile hat, es immer Dinge gibt die mit dem einen Ansatz âschönâ und mit dem anderen âschlechtâ (oder sogar: gar nicht) abgebildet werden können, und ĂŒber allem das Damoklesschwert der unbenutzbaren Ăber-Abstraktion schwebt, wenn man wirklich alles âsauber durchmodellierenâ willâŠ
Ich meinte natĂŒrlich, dass sich die Funktionen sich nicht zwangweise in Klassen befinden mĂŒssen. In Python befinden sie sich natĂŒrlich in Module, wo man auch Klassen stecken kann. In C++ befinden sich diese zu meist in Namespaces.
Zu dem Beispiel: Ich selbst wĂŒsste auch nicht, wie ich so etwas in Java abbilden sollte. Wenn dann wĂŒrde ich mal schauen, was andere APIs machen. Ich wĂŒrde aber dazu neigen, einen Mittelweg zwischen Abstrahierung und nicht Abstrahierung wĂ€hlen, weil alle Lösungen irgendwie nicht besonders gut aussehen. Ich glaube ich wĂŒrde am ehesten message.send() wĂ€hlen, auch wenn es auch nicht gut aussieht, weil dadurch die Message class mit dem Rest unnötig verdrahtet wird. Auch sieht es blödsinnig aus, eine Klasse mit dem $Verb + PrĂ€fix -er $Verb zu befehlen und die statische Methode sieht dort auch nicht gut aus. In Python jedenfalls wĂŒrde ich versuchen ein Modul namens msgs erstellen und dort alle Funktion hineinzustecken, die das senden und empfangen von Nachrichten betreffen.
Ja, das ist genau der Konflikt, den ich da sehe (und den er auch (soweit ich das verstehe, zumindest indirekt) kritisiert). Auch wenn die genauen Klassen und Bedeutungen und use-cases bei dieser sehr suggestiv gestellten Frage nicht klar sind, wĂŒrde man hinter einem âMessageâ-Objekt ja intuitiv irgendein Datenpaket verstehen - und zwar ein ziemlich âdummesâ. Es wĂ€re zwar âeher OOPâ, wenn man message.send()
schreiben wĂŒrde, als wenn man auf MyStaticUtilityMethods.send(message, sender, receiver)
zurĂŒckgreifen wĂŒrde (scnr :D). Aber wie du schon angedeutet hast, wĂŒrde das bedeuten, dass eine âMessageâ IHREN âSenderâ und IHREN âReceiverâ kennen mĂŒĂte. Auch wenn es sicher Szenarien geben kann, wo das sinnvoll ist, wĂŒrde es erstmal, fĂŒr mich persönlich und aus abstrakter Modellierungssicht zumindest ungĂŒnstig wirken. (Ganz oberflĂ€chlich: Es wĂŒrde sich zumindest sofort die Frage der MultiplizitĂ€t stellen. Hat jede Message nur EINEN Receiver, oder kann nicht dieSELBE Message auch mal an ZWEI Receiver geschickt werden? Etc. Ich finde, es gehört einfach nicht da rein). Eine Modellierung, von der ich glaube, dass sie âOKâ sein könnte, wĂ€re die angedeutete (aber IIRC nicht (und nichtmal Ă€hnlich) im Video vorkommende) âMessageTransferâ-Klasse. Keiner muss irgendwas kennen, keine unnötigen AbhĂ€ngigkeiten. Aber es gibt eben einen MessageTransfer, wo alles zusammenkommt, und eine Message von einem Sender zu einem Receiver geschickt wird.
(Es ist wie immer schwierig, so ein vermeintlich konkretes Bespiel auf so einer oberflĂ€chlichen, nicht-ausspezifizierten Ebene zu besprechen, und damit Gefahr zu laufen, andere Messages (sic) aus dem Video unter den Tisch fallen zu lassenâŠ)
Ich habe das Video nicht angesehen, ist wahrscheinlich nicht gut fĂŒr meinen Blutdruck.
Aber natĂŒrlich lĂ€sst sich aus einem schlechten Video nicht schlieĂen, dass im OO-Land alles eitel Sonnenschein wĂ€re. Das Hauptproblem, das ich mit OO-Programmierung habe, ist wenig ĂŒberraschend Vererbung. Dieses **eine **Konzept muss fĂŒr (mindestens) **drei **Ziele herhalten, die nicht immer miteinander kompatibel sind, und keine Sprache hat es bisher geschafft, diese Ziele flexibel und orthogonal zueinander abzubilden. Die Ziele sind:
Typing: Wenn ich ein Foo x
habe, ist es legal, x
einer Variable vom Typ Bar
zuzuweisen? Fast alle OO-Sprachen sind sich darin einig, dass man das darf, wenn Foo
von Bar
erbt (*). Ich kann mir durchaus vorstellen, dass es FĂ€lle gibt, wo es nĂŒtzlich wĂ€re zu âvererbenâ, aber die entsprechende Zuweisung trotzdem verboten sein sollte (etwa Timestamp
von Date
).
Schnittstelle: Welche Operationen kann ein Foo x
ausfĂŒhren? Aber die Vermischung mit der Typing-Frage erlaubt es nicht, das z.B. Teile der Schnittstelle (oder die gesammte Schnittstelle) wieder âverstecktâ werden können. WĂ€re es nicht cool, aus einer mutablen Collection eine immutable abzuleiten, in dem man auch bestimmte Methoden beim Vererben weglĂ€sst (und dafĂŒr entsprechende âkopierendeâ Varianten anbietet)?
Implementierung: âDonât repeat yourselfâ - Ăbernahme von vorhandenen Implementierungen. Ein Quadrat könnte durchaus Implementierungen von einem Rechteck ĂŒbernehmen, aber nicht dessen Schnittstelle, die eine unterschiedliche LĂ€nge und Breite erlaubt.
(*) Was auch immer das im jeweiligen Kontext heiĂen mag. Schon die EinfĂŒhrung parametrischer Typen (a.k.a. âGenericsâ) zeigt, dass die Frage nicht ganz so einfach zu beantworten ist, wie es scheint (z.B. Kovarianz, Kontravarianz, Invarianz)
[quote=Landei]1) Typing:[/quote]Mehrfachimplementierung von (von einander unabhÀngigen) Interfaces macht genau das.
[quote=Landei;131607]2) Schnittstelle: Welche Operationen kann ein Foo x ausfĂŒhren? Aber die Vermischung mit der Typing-Frage erlaubt es nicht, das z.B. Teile der Schnittstelle (oder die gesammte Schnittstelle) wieder âverstecktâ werden können.[/quote]Also eigentlich doch, wenn die Schnittstellen eben nicht von einander geerbt haben.
[quote=Landei;131607]WĂ€re es nicht cool, aus einer mutablen Collection eine immutable abzuleiten, in dem man auch bestimmte Methoden beim Vererben weglĂ€sst (und dafĂŒr entsprechende âkopierendeâ Varianten anbietet)?[/quote]Viel cooler wĂ€re es doch aus einer Immutable Collection eine Mutable Collecton durch HinzufĂŒgen der notwendigen Methoden abzuleitenâŠ
[quote=Landei;131607]3) Implementierung: âDonât repeat yourselfâ - Ăbernahme von vorhandenen Implementierungen. Ein Quadrat könnte durchaus Implementierungen von einem Rechteck ĂŒbernehmen, aber nicht dessen Schnittstelle, die eine unterschiedliche LĂ€nge und Breite erlaubt.[/quote]Ich sag nur âFavour Composition over Inheritanceâ.
Also in den seltensten FĂ€llen ist der Hammer schuld, wenn man seinen Daumen trifftâŠ
bye
TT
@Timothy_Truckle : ich stimme dir in allen Punkten zu. Beim Lesen von @Landei s Beitrag gingen mir dieselben LösungsansÀtze durch den Kopf - das sind ja auch die (zumindest in Java) etablierten Wege.
Zu 1) fiel mir spontan auch noch das Konzept von dynamisch typisierten Sprachen ein. Auch wenn ich die Sprache im Detail nicht kenne, soll smalltalk den Typ einer Klasse aus den vorhandenen Methoden ableiten und nicht aus einer Deklaration.
Zu 2) noch ein Anwendungsbeispiel, das auch in Java funktioniert: Read-Only-Interfaces.
Zum Thema aus einer mutablen eine immutable Collection (oder umgekehrt) abzuleiten: widerspricht das nicht dem Grundsatz der âis-aâ-Beziehung? Das funktioniert zwar (zumindest in eine Richtung), aber die Eigenschaft âVerĂ€nderbarâ oder âUnverĂ€nderbarâ ist elementar fĂŒr die Klasse und dĂŒrte daher mMn innerhalb einer Vererbungshierarchie nicht verĂ€ndert werden. Auch wenn durch Vererbung an dieser Stelle Code wiederverwendet werden könnte, wĂ€re Komposition der ârichtigeâ Weg.
Einerseits erinnert das dann an das Ars technica experiment, andererseits: Schon OK, sooo sehenswert ist das nicht. Recht wirr und lÀnglich⊠man kann seine Zeit sinnvoller nutzen.
Die genannten drei Punkte sind eine Sichtweise. Ich finde, dass die Punkte schon stark zusammenhĂ€ngen, aber ⊠das ist ja anscheinend das, was du kritisierst, und letztlich ist das vielleicht nur das Ergebnis der âPrĂ€gungâ durch klassiche Sprachen wie Java und C++. Deren Typsysteme haben ihre SchwĂ€chen, die man teilweise mit âAltlastenâ rechtfertigen kann, aber auch noch mit vielen anderen Dingen. Beide Sprachen wurde nicht mit dem Ziel entwickelt, eine âTyptheoretisch einwandfreie Spracheâ zu entwickeln, sondern eine Sprache zum âgetting things doneâ. (Zumindest Java. Zu C++ sage ich da jetzt mal nichts :rolleyes: hucrapst ). Die Schwierigkeiten und das Glatteis, auf das man sich da leicht begibt, erkennt man, wie du ja schon sagtest, bei den Generics. Die sind zwar âtyptheoretisch schönâ, aber immernoch nicht einwandfrei, und haben selbst eingefleischte Java-Fans an den Rand der Verzweiflung gebracht. Sie haben auch viel âhĂ€Ălichen Codeâ verursacht. Sowas wie Angelika Langers FAQ-Beitrag dazu spricht schon BĂ€nde, die Abschnitte zu Typinferenz in der JLS ebenso, die (gefĂŒhlt) meisten Regression-Bugs zwischen den Major-Java-Versionen (und Unterschiede zwischen dem Eclipse-Compiler und Javac) hĂ€ngen damit zusammen, dass es die Typinferenz bei irgendwelchen absurden Schachtelungen raushaut, und fĂŒr mich persönlich war es schon fast erschĂŒtternd, zu lesen, wass man mit Wildcards Typen bauen kann, die es nicht geben kann (und wo es eigentlich jeden Compiler bei dem Versuch, sie aufzulösen, raushauen mĂŒĂte). (ErschĂŒternd, aber nicht wirklich ĂŒberraschend, wenn man sieht, wie absurd komplex vermeintlich einfache Dinge sind).
Trotzdem denke ich, dass die drei genannten Punkte mit Java schon ârelativ sauberâ getrennt werden können. Nicht perfekt, ohne sowas wie Duck-Typing, und natĂŒrlich ist Java nicht JavaScript, wo man Funktionen eher an Objekte hĂ€ngt und sie weniger mit Klassen zu tun haben, aber ⊠es liefert einen ganz guten Trade-off zwischen GenerizitĂ€t, Sauberkeit und Einfachheit (wobei letzteres sehr wichtig ist - auch wenn es nicht cool klingt, wenn man das sagt).
Und ich sag nur:
public double getArea() {
return MyStaticUtilityMethods.computeArea(width, height);
}
Ich habe nicht gesagt, dass sich fĂŒr die angesprochenen Probleme keine Lösungen finden lassen, der Punkt ist vielmehr, dass viele der LösungsvorschlĂ€ge eben weg von âOOâ gehen. So stimme ich der alten Weisheit âComposition over Inheritanceâ ja zu, nur muss sich dann âInheritanceâ die Frage gefallen lassen, welches Problem sie denn eigentlich löst.
Auch was eine âis-aâ-Beziehung ist, ist in der Praxis nicht ganz klar. In der Theorie gibt es das LSP als Kriterium, nur wird das eben im richtigen Leben nicht immer eingehalten, weil es zu restriktiv ist. Und sobald die Typen komplizierter werden, ist der âist-einâ Ansatz sowieso zu simplistisch (âist eineâ List<Timestamp>
denn eine Collection<Date>
? - nein, ist es nicht, aber erklÀre das einmal einem Neuling)
Und manchmal möchte man auch nur das gleiche Verhalten, aber keine Vererbungsbeziehung (etwa physikalische Einheiten, die sich exakt so wie BigDecimal verhalten sollen, aber mit diesem nicht einfach verrechnet werden können). Haskell realisiert diese FunktionalitĂ€t ĂŒber newtype
. Sicher kann man hier auch âwrappenâ, aber das heiĂt in Sprachen ohne Delegation (was ein zu OO orthogonales Konzept ist), jede einzelne Methode manuell âweiterzuleitenâ.
[ot][quote=Timothy_Truckle;131353]Richtig absurd wirdâs dann ab 40:00[/quote]
So lange hast du das durchgehalten? :o)[/ot]
[quote=L-ectron-X]So lange hast du das durchgehalten?[/quote][OT]Bin verheiratet, also weitgehend leidensfĂ€higâŠ
:D[/OT]
bye
TT