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



  • 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?



  • finix schrieb:

    Java-Package == C++-Namespace
    Was auch einleuchten dürfte wenn man sich vor Augen hält was ein "Package" logisch gesehen ist.

    Dir ist es jedenfalls noch nicht eingeleuchtet. Ein package hat in Java weit mehr Sinn als eine namespace-Funktion. Ein package ist ein Paket von Klassen, eine einzeln sinnvolle Programmkomponente und die Klassen darin können sich gegenseitig Einblick in ihre Internas gestatten, was ein ähnlicher Mechanismus wie friend ist. In .Net wird diese Aufgabe von Assemblies erfüllt, in C++ gibt es gar kein Aquivalent dazu.

    Ein so offensichtlicher, fett geschriebener Fehler zeigt doch schon, dass du die Ideen wenigstens teilweise nicht verstanden hast.



  • finix schrieb:

    Java-Package == C++-Namespace

    Dass das falsch ist hat Optimizer ja schon gesagt. (package private...)

    Was ist den nun eigentlich ein Vorteil von freien Funktionen? Das man weniger scheiben muss?



  • Dingens schrieb:

    Was ist den nun eigentlich ein Vorteil von freien Funktionen? Das man weniger scheiben muss?

    In C++ ist ein Namespace offen eine Klasse hingegen geschlossen. Du kannst also beliebig viele neue Funktionen zu einem Namespace hinzufügen ohne das du dazu an einem zentralen Ort was ändern musst (oder neu übersetzen). Das ist der physische Unterschied. Der logische Unterschied besteht darin, dass eine freie Funktion besser verdeutlicht, dass sie unabhängig von der Repräsentation einer Klasse ist.
    Stark vereinfacht: wenn eine Klasse 10 Methoden hat und ich ändere die interne Repräsentation, dann muss ich 10 Methoden prüfen. Hat die Klasse nur 3 Methoden und dafür 7 freie Funktionen, so muss ich nur 3 Methoden bei der selben Änderung
    prüfen (wobei sich dabei natürlich rausstellen kann, dass ich 7 Methoden über 3 Hauptmethoden implementiert habe). Das ist genaugenommen natürlich kein Vorteil einer freien Funktion. Der Vorteil ergibt sich vielmehr aus der Unterscheidung zwischen zwei Arten von Methoden: eine die auf Daten zugreift und eine die nur die öffentliche Schnittstelle verwendet. In C++ kann ich diese Unterscheidung über die Verwendung von Memberfunktionen einerseits und freien Funktionen andererseits ausdrücken.

    In C++ kann eine Klasse mit minimalem Interface dank freier Funktionen zu einer Klasse mit einem (erweiterbaren) komfortabelen Interface (manche würde auch fettes Interface sagen) werden, ohne das dazu die Klasse verändert werden muss.

    Das gesagte bezieht sich natürlich nur auf C++ und ist keine Aussage über andere Sprachen. Mir ist allerdings die Diskussion hier sowieso nicht klar. Was hätte man davon, wenn man in einer Sprache wie Java aufeinmal freie Funktionen anbieten würde?
    Kommt man da nicht letztlich irgendwann zu der Frage:"was ist der Vorteil einer Ein-Paradigma-Sprache gegenüber einer Mehr-Paradigmen-Sprache?"?



  • HumeSikkins schrieb:

    Kommt man da nicht letztlich irgendwann zu der Frage:"was ist der Vorteil einer Ein-Paradigma-Sprache gegenüber einer Mehr-Paradigmen-Sprache?"?

    Nun versöhnt euch wieder vorm Osternest, lasst das Frickeln und Streiten und besinnet euch auf die wahren Werte des Lebens! Mein erstes Puzzlebuch Ostern | ISBN: 3473312932



  • Statt meine Klassen mit privaten Helferfunktionen zuzumüllen mache ich oft sowas:

    statt

    class Foo
    {
      void helper1(int);
      void helper2(string);
      void helper3(double);
      void helper(); //verwendet helper1,2,3
    };
    

    mache ich das so:

    class Foo
    {
      void helper();
    };
    
    //in foo.cpp
    namespace
    {
      void helper1(int){}
      void helper2(string){}
      void helper3(double){}
    }
    

    Meist braucht nur "helper" zugriff auf die ganzen Membervariablen und die kleinen Hilfsfunktionen von helper sind nur dazu da den code übersichtlicher zu gestalten indem details ausgelagert werden.



  • Funktionen sind auch nur Objekte.

    einObjekt := 'Hallo Welt!'.
    
    andersObjekt := [ :eachChar|
        Transcript show: eachChar ; cr.
    ].
    


  • Optimizer schrieb:

    finix schrieb:

    Java-Package == C++-Namespace
    Was auch einleuchten dürfte wenn man sich vor Augen hält was ein "Package" logisch gesehen ist.

    Dir ist es jedenfalls noch nicht eingeleuchtet. Ein package hat in Java weit mehr Sinn als eine namespace-Funktion. Ein package ist ein Paket von Klassen, eine einzeln sinnvolle Programmkomponente und die Klassen darin können sich gegenseitig Einblick in ihre Internas gestatten, was ein ähnlicher Mechanismus wie friend ist. In .Net wird diese Aufgabe von Assemblies erfüllt, in C++ gibt es gar kein Aquivalent dazu.

    Ein so offensichtlicher, fett geschriebener Fehler zeigt doch schon, dass du die Ideen wenigstens teilweise nicht verstanden hast.

    Ok, nochmal für die Leute die mit dem Begriff "Kontext" nicht klar kommen oder unfähig sind von irrelevanten Details zu abstrahieren:

    Java: Package -> package
    C++:  Package -> namespace
    ...
    


  • finix schrieb:

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

    Ich hatte bis jetzt immer den OO-Aspekt deiner Frage übersehen. Sorry.

    Naja, die Objektorientierung ist letztendlich eine Art Weltsicht. Guck da doch mal dein reales Umfeld an: Gibt es da Interaktionsmöglichkeiten, die nicht an irgendwelche Dinge oder abstrakte Konzepte gebunden sind? Gib mal das äquivalente Konzept zu einer "freien Funktion" in deinem echten Leben an.



  • Könntet ihr das Ganze mal vor dem Hintergrund einer wirklich objektorientierten Sprache sehen, wie z.B. Smalltalk. Dann wird auch meine Aussage meines letzten Posts ersichtlich.
    Nur weil Java zu beschränkt ist, um Funktionen als Objete zu behandeln, heißt das nciht, dass das generel so im Objektorientierten Universum sein muss.

    In einer Sprache wie Smalltalk ist eben alles ein Objekt, auch Funktionen (Codeblöcke mit Parametern).

    Gibt es globale Variablen, gibt es somit dann automatisch globale Funktionen und sie sind vollkommen oo, da es egal ist, ob die Variable auf einen String, eine Zahl oder eine Funktion verweist. Es stekt einfach ein Objekt dahinter, dass gewisse Nachrichten versteht und andere eben nicht.



  • finix schrieb:

    Optimizer schrieb:

    finix schrieb:

    Java-Package == C++-Namespace
    Was auch einleuchten dürfte wenn man sich vor Augen hält was ein "Package" logisch gesehen ist.

    Dir ist es jedenfalls noch nicht eingeleuchtet. Ein package hat in Java weit mehr Sinn als eine namespace-Funktion. Ein package ist ein Paket von Klassen, eine einzeln sinnvolle Programmkomponente und die Klassen darin können sich gegenseitig Einblick in ihre Internas gestatten, was ein ähnlicher Mechanismus wie friend ist. In .Net wird diese Aufgabe von Assemblies erfüllt, in C++ gibt es gar kein Aquivalent dazu.

    Ein so offensichtlicher, fett geschriebener Fehler zeigt doch schon, dass du die Ideen wenigstens teilweise nicht verstanden hast.

    Ok, nochmal für die Leute die mit dem Begriff "Kontext" nicht klar kommen oder unfähig sind von irrelevanten Details zu abstrahieren:

    Java: Package -> package
    C++:  Package -> namespace
    ...
    

    Alter Schwede... diese Details sind keineswegs irrelevant, weil packages Zugriffskontrolle zwischen Programmkomponenten regeln. Etwas in dieser Art gibt es in C++ nicht. Das sind ganz wesentliche Aspekte, die beim Design einer Anwendung beachtet werden müssen. Gerade dass es in C# auch namespaces gibt, das eigentliche Äquivalent zu packages aber Assemblies sind, sollte dir zu denken geben.



  • Helium schrieb:

    In einer Sprache wie Smalltalk ist eben alles ein Objekt, auch Funktionen (Codeblöcke mit Parametern).

    Ich kenne Smalltalk leider nicht. Kannst Du da mal ein kleines Beispiel bringen, inwiefern Funktionen da als Objekte behandelt werden bzw. wie man mit denen als Objekt umgeht?



  • finix = troll?



  • Bei Python ist auch alles nen Objekt



  • Optimizer schrieb:

    Alter Schwede... diese Details sind keineswegs irrelevant, weil packages Zugriffskontrolle zwischen Programmkomponenten regeln. Etwas in dieser Art gibt es in C++ nicht. Das sind ganz wesentliche Aspekte, die beim Design einer Anwendung beachtet werden müssen. Gerade dass es in C# auch namespaces gibt, das eigentliche Äquivalent zu packages aber Assemblies sind, sollte dir zu denken geben.

    Es ging um diese Frage:

    Gregor schrieb:

    Wie hälst Du deinen Code halbwegs strukturiert, wenn globale Funktionen eine nennenswerte Rolle spielen? ...

    Nicht darum dass Java-packages genau equivalent zu C++-Namespaces sind, nichtmal um die Sprache an sich sondern wie es man das strukturiert. Package im Sinne von "ein Paket von Klassen, Funktionalität, eine einzeln sinnvolle Programmkomponente."



  • @finix: Du versuchst hier auf einer Art Metaebene den Argumenten der anderen Diskussionsteilnehmer aus dem Weg zu gehen. Du sagst dir einfach "Alles, was die anderen bringen ist aus dem Kontext gerissen und sowieso nur auf unnötige Details bezogen.". ...oder ähnliches. Das kannst Du ja meinetwegen denken, aber wenn Du willst, dass Dich die anderen Diskussionsteilnehmer auf Dauer ernst nehmen, solltest Du etwas genauer erklären, warum welche Aussage in die falsche Richtung geht. Du gehst nämlich überhaupt nicht auf die sachlichen Argumentationen der anderen ein. Es ist, als ob man bei Dir gegen eine Wand redet: Man erhält keinerlei sinnvolle Reaktion zurück, auf der man aufbauen kann.

    Da kommt es dann natürlich auch dazu, dass jemand einen Vergleich "finix = troll?" macht. ...und Du kannst mal davon ausgehen, dass andere auch schon etwas in diese Richtung denken, auch wenn sie die Diskussion noch nicht aufgeben möchten.



  • finix schrieb:

    Es ging um diese Frage:

    Gregor schrieb:

    Wie hälst Du deinen Code halbwegs strukturiert, wenn globale Funktionen eine nennenswerte Rolle spielen? ...

    Nicht darum dass Java-packages genau equivalent zu C++-Namespaces sind, nichtmal um die Sprache an sich sondern wie es man das strukturiert. Package im Sinne von "ein Paket von Klassen, Funktionalität, eine einzeln sinnvolle Programmkomponente."

    Ja. ...und du hast bei der Beantwortung der Frage auch alles aus dem Kontext gerissen. Da gab es nämlich auch noch ein

    finix schrieb:

    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?

    davor. Du hast bei der Beantwortung der Frage keine Namespaces zur Verfügung. 😉



  • Any schrieb:

    Statt meine Klassen mit privaten Helferfunktionen zuzumüllen mache ich oft sowas:

    statt

    class Foo
    {
      void helper1(int);
      void helper2(string);
      void helper3(double);
      void helper(); //verwendet helper1,2,3
    };
    

    mache ich das so:

    class Foo
    {
      void helper();
    };
    
    //in foo.cpp
    namespace
    {
      void helper1(int){}
      void helper2(string){}
      void helper3(double){}
    }
    

    Meist braucht nur "helper" zugriff auf die ganzen Membervariablen und die kleinen Hilfsfunktionen von helper sind nur dazu da den code übersichtlicher zu gestalten indem details ausgelagert werden.

    wo ist hier den der Vorteil? die Helper-Methoden sind doch eh private, da ist es vollkommene egal ob ich 100 oder 1000 davon habe.

    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.

    globale Funktionen sind für mich Funktionen die keinen Kontex haben, zu nichts in Bezug stehen. Ein Namespace stellt aber ein Bezug her.

    Die Funktion sort(bla[] x) hat für mich keinen Bezug. Ich habe absolut keine Ahnung was sie macht, sortiert sie auf- oder absteigend? Oder mit Quiksort oder mit Mergesort?
    Quicksort.sort(bla[] x). Aha, da wird einem sofort klar, das die statische-Methode nach dem Quicksort-Prinzip sortiert.

    ein weiterer Vorteil:
    wenn ich eine Funktion sin(double x) habe im system.h und irgendwann importiere ich winsystem.h und die Header hat auch eine sin(double x)-Funktion, welche Funktion soll den nu der Compiler benutzen?

    Fazit: Globale Funktionen sind vollkommen Sinnfrei und führen nur in größeren Projekten zum totalen Chaos. Deswegen wurden ja in C++ die Namespaces eingeführt und von Java, C# und co. übernommen und verbessert.


Anmelden zum Antworten