An die OOP-Fetischisten- & Dogmatiker: Freie Funktionen!?



  • Funktionen halt (-> alle Funktionen, die keine Methoden sind).



  • .filmor schrieb:

    Und?!

    Eh? 😕
    Weißt du mit dem ?-Zeichen nichts anzufangen oder was?

    Artchi schrieb:

    Was sind denn freie Funktionen?

    Funktionen die keine Methoden sind.



  • Doch, aber ich ignoriere fettgedruckte Überschriften aus Prinzip 😉

    Gegen sie spricht, dass man versucht, alle Funktionalität in den Beziehungen zwischen den Objekten auszudrücken. Funktionen, die es durchaus noch gibt, falls man nicht krampfhaft versucht, rein objektorientiert zu programmieren, s. <algorithm>, sind allgemeine Verfahren, die nicht zum Objekt gehören, sondern etwas mit diesem anstellen.



  • Was spricht aus OOP-Sicht gegen Freie Funktionen?

    Pragmatisch gesprochen z.B.das viele OOP-only Sprachen sie gar nicht unterstützen.

    Ansonsten würde ich aber sagen: eine Funktion, die nicht auf die interne Repräsentation eines Objekts zugreifen muss, sollte, wenn möglich, frei sein. Gute Gründe dafür liefert Scott Meyers in seinem Artikel:
    How Non-Member Functions Improve Encapsulation



  • .filmor schrieb:

    Doch, aber ich ignoriere fettgedruckte Überschriften aus Prinzip 😉

    Heh. Ich hab's extra fett gestaltet damit man weiß worum's geht und sich nicht auf mein nachgelagertes blablah stürzt 😃

    .filmor schrieb:

    Gegen sie spricht, dass man versucht, alle Funktionalität in den Beziehungen zwischen den Objekten auszudrücken. Funktionen, die es durchaus noch gibt, falls man nicht krampfhaft versucht, rein objektorientiert zu programmieren, s. <algorithm>, sind allgemeine Verfahren, die nicht zum Objekt gehören, sondern etwas mit diesem anstellen.

    Und was genau ist nun so schlecht daran?
    Was bring z.B. java.lang.Math für Vorteile? (vor 5.0, oder 1.5, vor free funcs in j aka static import...)



  • HumeSikkins schrieb:

    Was spricht aus OOP-Sicht gegen Freie Funktionen?

    Pragmatisch gesprochen z.B.das viele OOP-only Sprachen sie gar nicht unterstützen.

    Klar, ging mir aber nicht so sehr (oder gar nicht) um's pragmatische sondern um die Begründung, falls vorhanden, die Idee, Ideologie die dahinter steckt (oder auch nicht) - "The OOP Mindset" sozusagen.

    HumeSikkins schrieb:

    Ansonsten würde ich aber sagen: eine Funktion, die nicht auf die interne Repräsentation eines Objekts zugreifen muss, sollte, wenn möglich, frei sein. Gute Gründe dafür liefert Scott Meyers in seinem Artikel:
    How Non-Member Functions Improve Encapsulation

    Ja, danke, aber das ist mir alles durchaus bekannt & bewusst.

    Mir geht's eher um die Begründung der OOP-Puristen gegen freie Funktionen...



  • Gib ein Beispiel. Falls du den Mischmasch à la C++ meinst: da ist der Namespace von freien Funktionen einfach unbekannt.

    Dadurch dass du dir mit deinen Headern deine Funktionen sonstwoher holst, ist gar nicht zu überblicken in welchem Kontext jetzt welche Funktion steht.

    Wenn du Methoden nur innerhalb von Klassen zulässt, hat sich das Problem gegessen: eine Methode ist IMMER einem bestimmten Objekt zugehörig, und die Definition dieses Objekts ist garantiert einzigartig.

    Im übrigen widerspricht es halt der OOP Vorstellung, dass ALLES in der Welt ein Objekt ist. In Java ist z.B. selbst eine Klasse ein Objekt. Eine Eigenschaft oder ein Verhalten ohne dazugehöriges Objekt ist irgendwie sinnfrei. Wenn du "laufen" sagst, musst du auch sagen, was dies tut.



  • finix schrieb:

    Was bring z.B. java.lang.Math für Vorteile? (vor 5.0, oder 1.5, vor free funcs in j aka static import...)

    java.lang.Math wird ja eher als so eine Art "Namespace" genutzt. Die Methoden darin sind alle statisch. Man kennt dadurch also gleich den Ort, wo man etwas über die Methoden erfahren kann. Was wäre denn die Alternative? So eine Art globale Funktionen, wo keiner weiß, wo die definiert sind, wo die Dokumentation dazu zu suchen ist, was es in diesem Themengebiet eigentlich alles für Funktionen gibt usw.? Aus diesem Grund sind static imports in Java 5.0 auch nicht gerne gesehen: Man kann mit denen nicht mehr so schnell herausfinden, wo eine Methode zu finden ist (es sei denn, man hat eine IDE, die das ausgleicht).



  • std::sort auf Iteratoren ist in C++ zehnmal mächtiger als wenn man das Ganze auf die Containerklassen beschränkt hätte. Ich definier mir nen neuen Containertyp, definier nen random-access Iterator dafür und das Ding läuft.

    Wenn ich dafür extra von ner Basisklasse ableiten müsste und dann noch die genau gleichen Membernamen verwenden müsste, etc. dann hätte ich viel mehr von der Kapselung verloren nach außen.

    Ja das ganze hätte man auch in eine Klasse "algorithms" packen können. Aber wozu? Es ist nunmal kein Objekt, es ist einfach nur ein simpler Namespace.



  • mon schrieb:

    Gib ein Beispiel. Falls du den Mischmasch à la C++ meinst: da ist der Namespace von freien Funktionen einfach unbekannt.

    Schwachfug.

    mon schrieb:

    Dadurch dass du dir mit deinen Headern deine Funktionen sonstwoher holst, ist gar nicht zu überblicken in welchem Kontext jetzt welche Funktion steht.

    Ja, super Punkt. Genau aus diesem Grund importiere ich in Java auch keine Packages die ich nicht selber kenne. Oder so in der Richtung. 🙄

    mon schrieb:

    Wenn du Methoden nur innerhalb von Klassen zulässt, hat sich das Problem gegessen: eine Methode ist IMMER einem bestimmten Objekt zugehörig, und die Definition dieses Objekts ist garantiert einzigartig.

    Häh?

    mon schrieb:

    Im übrigen widerspricht es halt der OOP Vorstellung, dass ALLES in der Welt ein Objekt ist. In Java ist z.B. selbst eine Klasse ein Objekt. Eine Eigenschaft oder ein Verhalten ohne dazugehöriges Objekt ist irgendwie sinnfrei. Wenn du "laufen" sagst, musst du auch sagen, was dies tut.

    Das ist genau Kern meiner Frage: warum widerspricht es der OOP-Vorstellung?



  • Gregor schrieb:

    finix schrieb:

    Was bring z.B. java.lang.Math für Vorteile? (vor 5.0, oder 1.5, vor free funcs in j aka static import...)

    java.lang.Math wird ja eher als so eine Art "Namespace" genutzt. Die Methoden darin sind alle statisch. Man kennt dadurch also gleich den Ort, wo man etwas über die Methoden erfahren kann. Was wäre denn die Alternative? So eine Art globale Funktionen, wo keiner weiß, wo die definiert sind, wo die Dokumentation dazu zu suchen ist, was es in diesem Themengebiet eigentlich alles für Funktionen gibt usw.? Aus diesem Grund sind static imports in Java 5.0 auch nicht gerne gesehen: Man kann mit denen nicht mehr so schnell herausfinden, wo eine Methode zu finden ist (es sei denn, man hat eine IDE, die das ausgleicht).

    Versteh mich nicht falsch, aber was du hier schreibst ist absoluter Unfug. Genau wegen solchen Äußerungen hatte ich die Formulierungen "Dogmatiker", "Fetischisten" & "Extremisten" verwandt.

    Dein erster Satz spiegelt genau meinen Eindruck wieder - weil es dass adequate Sprachmittel, sprich freie Funktionen in einem Namespace nicht gibt wird ein Objekt hergenommen und mit statischen Methoden bevölkert.

    Mit dieser Erklärung, auch wenn du nicht so deutlich wurdest, hättest du es belassen sollen; aber nein, du verteidigst diese Unzulänglichkeit nicht nur (was, gute Argumente vorausgesetzt, nichts schlimmes ist) sondern preist diese Einschränkung als Vorteil an (was ziemlich albern ist).

    Und wenn du so gegen static imports wetterst lass dir gesagt sein dass man auch in z.B. C++ auf using-Direktiven verzichten kann und versuch dich bitte nochmal an einer Antwort.



  • 3dckopierer schrieb:

    std::sort auf Iteratoren ist in C++ zehnmal mächtiger als wenn man das Ganze auf die Containerklassen beschränkt hätte. Ich definier mir nen neuen Containertyp, definier nen random-access Iterator dafür und das Ding läuft.

    Wenn ich dafür extra von ner Basisklasse ableiten müsste und dann noch die genau gleichen Membernamen verwenden müsste, etc. dann hätte ich viel mehr von der Kapselung verloren nach außen.

    Ja das ganze hätte man auch in eine Klasse "algorithms" packen können. Aber wozu? Es ist nunmal kein Objekt, es ist einfach nur ein simpler Namespace.

    Das statisch in eine Klasse zu packen ist ja auch nicht objektorientiert. Korrekt wäre es, eine Sorter-Klasse zu haben, die einen bestimmten Algorithmus implementiert und mit einem bestimmten Comparer (oder was der Algorithmus braucht) instanziert wird. Das nennt sich dann übrigens das Strategy-Pattern.



  • Optimizer schrieb:

    Korrekt wäre es, eine Sorter-Klasse zu haben, die einen bestimmten Algorithmus implementiert und mit einem bestimmten Comparer (oder was der Algorithmus braucht) instanziert wird. Das nennt sich dann übrigens das Strategy-Pattern.

    Das mach ich übrigens ganz häufig. 😃 ...aber ich bin ja wohl auch ein "OOP-Dogmatiker, -Fetischist & -Extremist"! 😮 🤡



  • Optimizer schrieb:

    Das statisch in eine Klasse zu packen ist ja auch nicht objektorientiert. Korrekt wäre es, eine Sorter-Klasse zu haben, die einen bestimmten Algorithmus implementiert und mit einem bestimmten Comparer (oder was der Algorithmus braucht) instanziert wird. Das nennt sich dann übrigens das Strategy-Pattern.

    Was ist jetzt genau der Vorteil gegenüber

    template <class RandomAccessIterator, class StrictWeakOrdering>
    void sort(RandomAccessIterator first, RandomAccessIterator last,
              StrictWeakOrdering comp);
    

    😕



  • Gregor schrieb:

    Optimizer schrieb:

    Korrekt wäre es, eine Sorter-Klasse zu haben, die einen bestimmten Algorithmus implementiert und mit einem bestimmten Comparer (oder was der Algorithmus braucht) instanziert wird. Das nennt sich dann übrigens das Strategy-Pattern.

    Das mach ich übrigens ganz häufig. 😃 ...aber ich bin ja wohl auch ein "OOP-Dogmatiker, -Fetischist & -Extremist"! 😮 🤡

    Schön dass ihr euch einig seid. Ich steh auch auf das Strategy-Pattern. Ganz toll.

    Könntest du jetzt auf mein Posting eingehen?
    Bereitet dir das Probleme oder fühlst du dir einfach auf den Schlips getreten?



  • finix schrieb:

    Optimizer schrieb:

    Das statisch in eine Klasse zu packen ist ja auch nicht objektorientiert. Korrekt wäre es, eine Sorter-Klasse zu haben, die einen bestimmten Algorithmus implementiert und mit einem bestimmten Comparer (oder was der Algorithmus braucht) instanziert wird. Das nennt sich dann übrigens das Strategy-Pattern.

    Was ist jetzt genau der Vorteil gegenüber

    template <class RandomAccessIterator, class StrictWeakOrdering>
    void sort(RandomAccessIterator first, RandomAccessIterator last,
              StrictWeakOrdering comp);
    

    😕

    Der Sorter ist zur Laufzeit austauschbar. Und es ist natürlich objektorientierter, weil der Algorithmus selber in ein Objekt gefasst ist. Das muss nicht jeder als Vorteil sehen, aber die Frage war ja an OOP-Dogmatiker gestellt.



  • finix schrieb:

    Dein erster Satz spiegelt genau meinen Eindruck wieder - weil es dass adequate Sprachmittel, sprich freie Funktionen in einem Namespace nicht gibt wird ein Objekt hergenommen und mit statischen Methoden bevölkert.

    Es existiert kein Objekt von Math. Die Methoden in Math sind an kein Objekt gebunden, es muss keins erzeugt werden, um diese Methoden zu nutzen. Die Nutzung der Klasse ist hier in der Tat äquivalent zur Nutzung eines Namespaces. Was da vorhin über "Klassen sind in Java selbst Objekte" gesagt wurde, ist auf einer ganz anderen ebene. Ich vermute mal, deshalb bist Du hier auf den Begriff Objekt gekommen.

    Vielleicht bin ich etwas zu pragmatisch, wenn ich sage, dass man Klassen einfach so als Namespace verwenden kann. Aber sag mir doch mal, inwiefern das unpassend ist. Was bietet Dir ein Namespace, was dir eine Klasse nicht bieten kann? Die Nutzung ist doch sehr ähnlich. ...nur, dass ne Klasse im Zweifelsfall auch mehr kann.

    Insofern braucht eine Sprache wie Java keine Namespaces. das wäre ein Feature, das bereits völlig äquivalent durch ein anderes Sprachfeature abgedeckt ist. Da fehlt nichts als "adäquates Sprachmittel".

    finix schrieb:

    Mit dieser Erklärung, auch wenn du nicht so deutlich wurdest, hättest du es belassen sollen; aber nein, du verteidigst diese Unzulänglichkeit nicht nur (was, gute Argumente vorausgesetzt, nichts schlimmes ist) sondern preist diese Einschränkung als Vorteil an (was ziemlich albern ist).

    Ok, könntest Du mir nochmal sagen, inwiefern das eine Einschränkung darstellt? Ich hatte das mit globalen Funktionen verglichen und halte weiterhin alles was ich dazu gesagt habe für richtig. Globale Funktionen haben Nachteile, was die ganz praktische Nutzung betrifft. Darüber kann man sich ruhig im Klaren sein.

    finix schrieb:

    Und wenn du so gegen static imports wetterst lass dir gesagt sein dass man auch in z.B. C++ auf using-Direktiven verzichten kann und versuch dich bitte nochmal an einer Antwort.

    Ich habe keine Äußerung zu C++ gemacht?!

    PS: Mit "globalen Funktionen" meinte ich bisher immer Funktionen, die sich in keinem Namespace befinden und auch nicht an eine Klasse oder ähnliches gebunden sind. Weiß nicht, ob die allgemeine Definition davon anders ist. Vielleicht liegt da ein gewisses Missverständnis zwischen uns vor.



  • Gregor schrieb:

    [java.lang.Math]

    Ok, mal anders herum: angenommen Java würde Namespaces und freie Funktionen bieten, was würde für Math als Klasse (meinte auch vorhin eigentlich nicht Objekt sondern Klasse) sprechen?

    Gregor schrieb:

    PS: Mit "globalen Funktionen" meinte ich bisher immer Funktionen, die sich in keinem Namespace befinden und auch nicht an eine Klasse oder ähnliches gebunden sind. Weiß nicht, ob die allgemeine Definition davon anders ist. Vielleicht liegt da ein gewisses Missverständnis zwischen uns vor.

    Auch gut, was ist an "globalen Funktionen" so furchtbar schlimm?



  • finix schrieb:

    Gregor schrieb:

    [java.lang.Math]

    Ok, mal anders herum: angenommen Java würde Namespaces und freie Funktionen bieten, was würde für Math als Klasse (meinte auch vorhin eigentlich nicht Objekt sondern Klasse) sprechen?

    Gregor schrieb:

    PS: Mit "globalen Funktionen" meinte ich bisher immer Funktionen, die sich in keinem Namespace befinden und auch nicht an eine Klasse oder ähnliches gebunden sind. Weiß nicht, ob die allgemeine Definition davon anders ist. Vielleicht liegt da ein gewisses Missverständnis zwischen uns vor.

    Auch gut, was ist an "globalen Funktionen" so furchtbar schlimm?

    Naja, ich habe das, was ich sagen wollte, wohl auf der falschen Ebene gesagt, also hier nochmal anders:

    1. Ich halte es für gut, wenn man im Code feststellen kann, wo der Source zu einer Funktion oder Methode oder Klasse oder sonstigem zu finden ist. Man kann dann schnell an den entsprechenden Stellen nachgucken, falls man irgendetwas über ein Methode oder so herausfinden möchte. In Java wird das durch zwei Dinge realisiert. Zum einen stehen Klassennamen mit den Namen der Dateien in Beziehung, in denen sie stehen und zum anderen steht die Verzeichnisstruktur mit der Packagestruktur in Beziehung. Ein Konzept wie "globale Funktionen" lässt sich mit solchen Beziehungen nur schwer in Einklang bringen. Deswegen halte ich es für nicht so gut. Auch ein static import macht die Navigation im Code nicht gerade einfacher, deshalb sehe ich auch dieses Feature eher skeptisch. Natürlich gibt es immer Ausnahmen: Wenn man in einer Methode 20 mal ein "Math." schreiben muss, dann kann ein static import schon Sinn machen. Aber man sollte damit IMHO sparsam umgehen. Ähnlich verhält es sich mit dem Import von Klassen: Das ist nichts, was die Navigation im Code erleichtert, allerdings würde hier die Navigation sehr stark auf Kosten der Lesbarkeit gehen, da Packagenamen oft recht lang sind.

    2. Ich halte es für gut, wenn man seinen Code thematisch hierarchisch strukturiert. Damit meine ich wiederum nicht nur die Klassenstruktur, sondern auch den Ort des Codes. Solche Hierarchien helfen einem dabei, den Code noch halbwegs zu überblicken, wenn er komplexer wird. Ein Konzept wie "globale Funktionen" steht hierzu wiederum im Widerspruch. "Wie hieß die Funktion noch gleich, die ich jetzt aufrufen will?" Wenn man bei so einer Frage eine entsprechende Hierarchie gegeben hat, kann man viel schneller nachsehen und das Problem lösen. Man kann sich auch insgesamt viel schneller ein Bild des Codes oder eines Teils des Codes machen, weil man weiß, wo man nachgucken muss.

    Das ist jetzt eine sehr Java-bezogene Darstellung meiner Sichtweise und die lässt sich somit nicht direkt auf C++ übertragen. Da gibt es ja zum Beispiel keine Package-Struktur. Und die Namespaces und Klassen und so sind eh nicht an die darunterliegenden Dateien bzw. die Verzeichnisstruktur gebunden. Insofern lassen sich Konzepte wie globale Funktion in C++ sicherlich besser rechtfertigen. Die ganze Vorgehensweise beim Programmieren wird dort anders sein. Meine Beiträge in diesem Thread sind somit nur auf Java bezogen. Aber für Java sind globale Funktionen aus den oben genannten Gründen nicht angebracht.

    Jetzt mal die Gegenfrage: Wie hälst Du deinen Code halbwegs strukturiert, wenn globale Funktionen eine nennenswerte Rolle spielen? Wie gehst Du dann mit der Komplexität des Sourcecodes um? Oder bezieht sich bei dir alles auf kleine Projekte, die man eh sofort überblickt?



  • Sorry dass ich mich so kurz fasse, aber 1. will ich noch weg & 2. machst du's mir recht leicht:

    Gregor schrieb:

    Jetzt mal die Gegenfrage: Wie hälst Du deinen Code halbwegs strukturiert, wenn globale Funktionen eine nennenswerte Rolle spielen? Wie gehst Du dann mit der Komplexität des Sourcecodes um? Oder bezieht sich bei dir alles auf kleine Projekte, die man eh sofort überblickt?

    Java-Package == C++-Namespace
    Was auch einleuchten dürfte wenn man sich vor Augen hält was ein "Package" logisch gesehen ist.
    Die Doku aufzutreiben ist auch kein allzu großer Akt, und auch nicht unbedingt immer so dramatisch solang man geschickte Bezeichner wählt und sich darauf verlassen kann dass das 'versprochene' korrekt implementiert ist. Ist ja auch nicht so dass man freie Funktionen nirgendwo included/importiert/your-keyword-here... oder qualifizierst du in Java sämtliche Klassen explizit?


Anmelden zum Antworten