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



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



  • Gregor schrieb:

    @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.

    Mir ist nicht ganz klar was du hier mit "Metaebene" meinst, aber von meinem Verständnis des Wortes her wär damit wohl etwas wie "Zitat: 'finix = troll'" gemeint.

    Aber ich sehe was du meinst:

    Gregor schrieb:

    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?

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

    Hatte das gestern komplett falsch gelesen; war ein wenig abgelenkt und hab mehr oder weniger nur globale Funktionen in Anführungszeichen gedeutet... dachte der Begriff "freie Funktionen" war nicht mehr missverständlich. Ging mir also, wie schon zuvor erklärt eigentlich nur um die Antwort auf deine konkrete Frage, spricht abstrakt "Package = logische Programmeinheit" bildet sich in C++ auf Namespaces ab wie es sich in Java auf Packages abbildet.
    Von daher seh ich natürlich auch wie für Opti Details die für mich im Kontext irrelevant erschienen durchaus alles andere als irrelevant sind.



  • Gregor schrieb:

    @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.

    /signed



  • finix schrieb:

    Mir ist nicht ganz klar was du hier mit "Metaebene" meinst, aber von meinem Verständnis des Wortes her wär damit wohl etwas wie "Zitat: 'finix = troll'" gemeint.

    Nein, eigentlich nicht. Er weist dich nur daraufhin nicht zu viel zu abstrahieren wo es nichts zu abstrahieren gibt.

    MfG SideWinder



  • DEvent schrieb:

    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.

    Der Header ist kleiner und damit übersichtlicher, wodurch die Dokumentation ebenfalls kürzer ausfällt und diese Funktionen nicht in der Benutzerdoku auftauchen.



  • DEvent schrieb:

    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.

    Was was ist wenn helper1-3 nur Implementationsdetails von helper sind?
    Was ist bei universell einsetzbaren Klassen, wenn's wirklich auf Hunderte von solchen Methoden hinausläuft?



  • SideWinder schrieb:

    finix schrieb:

    Mir ist nicht ganz klar was du hier mit "Metaebene" meinst, aber von meinem Verständnis des Wortes her wär damit wohl etwas wie "Zitat: 'finix = troll'" gemeint.

    Nein, eigentlich nicht. Er weist dich nur daraufhin nicht zu viel zu abstrahieren wo es nichts zu abstrahieren gibt.

    Vielleicht solltest du mal nachschlagen was "Metaebene" bedeutet.



  • Gregor schrieb:

    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?

    Man kann einem Objekt eine Nachricht schicken. Wie ein Objekt auf eine Nachricht reagiert ist durch die gleichnamige Methode in der Klasse definiert. Solche Nachrichten können auch Argumente enthalten.

    Naja. Eine Zahl kann man z.B. mit einem Zahlenliteral angeben und einer Variable zuweisen:

    foo := 42.
    

    Code Kann man zu Blöcken zusammen fassen [...].

    Da alles ein Obejkt ist, gilt das natürlich auch für einen Codeblock:

    foo := [ ... ].
    

    Das wird sehr oft ausgenutzt in Smalltalk. Z.b. Gibt es keine Schleifen oder bedingte Verzweigungen, denn wir wollen ja Objektorientiert und nicht imperativ programmieren. Boolsche Objekte verstehen die Nachricht ifTrue:ifFalse: .

    foo < bar liefert z.B. ein solches Objekt. Es hat entweder den Typ True oder den Typ False (Beide Abgeleitet von Boolean).

    Die implementierung der Methode ifTrue:ifFalse: in der Klasse True sieht so aus, dass einfach das erste Argument ausgeführt wird und das zweite ignoriert wird, die implementierung in der Klasse False führt das zweite Argument aus und ignoriert das erste.

    Man schickt also einfach die Nachricht ifTrue:ifFalse: an einen solchen boolschen Ausdruck mit zwei Codeblöcken als Argument.

    foo < bar ifTrue:  [ Transcript show: 'foo ist kleiner als bar'. ]
              ifFlase: [ Transcript show: 'foo ist nicht kleiner als bar'. ].
    

    Das nur als Beispiel zur Verwendung von Codeblöcken als Objekt.

    Ein solcher Codeblock kann wiederum Argumente entgegennehmen.

    [:argument | "eigentlicher Code"]
    

    Das kann man z.B. wie folgt verwenden:

    1 to: 20 do: [:x | Transcript show: x asString. ].
    

    Es wurde die Nachricht to🇩🇴 mit den Argumenten 20 und [:x | Transcript show: x asString. ] an das Objekt 1 geschickt. Das ganze entspricht quasi einer Schleife. Die Methode ruft den Codeblock entsprechend oft auf. Dabei wird dann jeweils an den Codeblock der Wert übergeben, der bei der Iteration gerade aktuell ist.

    Ich gebe zu, das war sehr knapp und ich habe auch alles einfach so runtergeschrieben, wie es mir gerade in den Kopf gekommen ist, aber ich hoffe, du konntest dennoch etwas verstehen. Alles ist ein Objekt. Code bzw. Codeblöcke bilden hierbei keine Ausnahme. Da man in Variablen Verweise auf Objekte speichern kann, kann man das auch mit Codeblöcken.



  • DEvent schrieb:

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

    Ein Namespace ordnet nur. std:: sagt mir nur, dass darin alle Funktionen, Klassen und Objekte stehen, die der Standard definiert.

    DEvent schrieb:

    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.

    Und was hast du davon? Es bringt dir nichts, zu wissen, wie sortiert wird (es sei denn, du weißt wie das Feld in etwa aussieht, aber das sollte sehr selten sein). Die Sortierrichtung gibt man mittels eines Funktors (der auch nur die ver-OOP-te (und "mächtigere") Variante der Funktion ist) oder per Iterator-Art an. Das ganze ist deutlich flexibler und sinnvoller. Man einigt sich auf Konventionen (in diesem Fall den Standard), die bestimmte Eckdaten bereitstellen (z.B. Laufzeit O(n*log(n))). Hauptsache ist, der Container (nicht nur Arrays!) ist nachher nach den eingegebenen Kriterien sortiert.

    DEvent schrieb:

    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?

    Die im richtigen Namespace. Der globale Namespace wird nicht (ben|verschm)utzt. Selbst C-Leute benutzen (meist) ein Präfix, also Lightnamespaces.

    DEvent schrieb:

    und von Java, C# und co. übernommen und verbessert.

    Das halte ich für ein Gerücht. Klassen als reine Namespaces zu missbrauchen ist nicht sinnvoll.



  • finix schrieb:

    Was was ist wenn helper1-3 nur Implementationsdetails von helper sind?
    Was ist bei universell einsetzbaren Klassen, wenn's wirklich auf Hunderte von solchen Methoden hinausläuft?

    es doch vollkommen egal, weil die helper-methoden nicht public sind 💡
    Den Satzt mit der universell einsetzbaren Klassen verstehe ich nicht. Die Klasse Collection hat sicherlich auch zig private-Methoden und ist trotzdem recht universell einsetzbar.
    Was soll der Begriff "universell einsetzbar" überhaupt? Eine Klasse ist eine Sammlung von Algorithmen um eine ganz bestimmte Aufgabe zu erfüllen. Du verlangst ja auch nicht von einem Auto das es fliegt oder ?

    Der Header ist kleiner und damit übersichtlicher,

    das ist einzig nur ein Nachteil von C++. Header Dateien trennen nunmal nicht das Interface von der Implementation. Im Idealfall hast du nur ein Interface zur verfügung, in dem nur public Methoden auftauchen. Das ist auch eine Idee bei OOP. Die Trennung von unwichtigeren Sachen (implementation) von den wichtigen (interface).

    wodurch die Dokumentation ebenfalls kürzer ausfällt und diese Funktionen nicht in der Benutzerdoku auftauchen.

    seit wann tauchen private Methoden in der Docu auf? Wenn es bei so ist, dann frag ich mich welcher Depp private Methoden dokumentiert. Private Methoden sollten dem User der Klasse überhaupt nie sichtbar sein.


Anmelden zum Antworten