static, Singleton, etc. verbieten?



  • Lallenbubbler schrieb:

    Aber wieso sollte man Sinus krampfhaft an ein Objekt hängen wollen? Sinus ist doch keine Methode von Zahl. Sinus ist eine Funktion. Was soll da rauskommen? 3.sinus() ändert dann den Wert von 3 und es gibt keine 3 mehr im Programm? 🤡

    Also ich hätte schon Interesse daran, es in einer Richtung zu vereinfachen.

    Baum b;
    cout<<b.masse()<<'\n';
    double d;
    cout<<d.sqrt()<<'\n';
    

    oder

    double d;
    cout<<sqrt(d)<<'\n';
    Baum b;
    cout<<masse(b);
    

    Aber auf keinen Fall das:

    Baum b;
    cout<<Botanik.masse(b)<<'\n';
    double d;
    cout<<Math.sqrt(d)<<'\n';//Hä? Was soll das denn?
    


  • Ich glaube mir gefällt:

    sqrt(d)
    

    am B/besten. Entspricht "send sqrt to d".

    MfG SideWinder





  • Das Problem könnte man doch einfach lösen, indem man

    d.sqrt();
    sqrt(d);
    

    parallel erlaubt. Dann könnte man auch ohne Interfaceerweiterungen einfach eine freie Funktion definieren, die sich genau so wie eine Methode verhält. Ist auch aus einer funktionalen Perspektive wesentlich schöner. Nebenbei umgeht man elegant das Problem, dass bestimmte Methoden besser in der "x.y()" schreibweise aussehen als andere, die besser mit "y(x)" aufgerufen werden.



  • otze schrieb:

    parallel erlaubt.

    Perl macht das so.



  • Welche sehen denn in der obj.method()-Syntax besser aus?

    MfG SideWinder



  • @otze:

    Das verhindert dann aber wieder den eigentlich Sinn der Debatte - Nämlich die eigene Vorstellung von der idealen Objektorientierung allen anderen aufzuzwingen. Und dieses Prinzip bringt nahezu jede Diskussion in diese Richtung zwangsläufig mit sich, denn sonst hättest du einfach in jeder Sprache alles verfügbar ...

    Ich persönlich ziehe sqrt(3) eindeutig 3.sqrt vor. Denn die Wurzel ist keine der Zahl 3 eigenen Eigenschaft sondern letztlich eine algebraisch definierte und auf die konkrete Zahl angewandte Funktion. Andernfalls könnte man jeden injektiven Algorithmus als feste Eigenschaft einer jeden einzelnen Zahl auffassen.

    P.s. Ich wäre nebenbei mal dafür, die Grundrechenarten Radizieren und Potenzieren in den normalen Sprachumfang (sprich als eingebauten Operator) zur Verfügung zu stellen. In Hochsprachen scheint mir das sehr sinnvoll.



  • SideWinder schrieb:

    Welche sehen denn in der obj.method()-Syntax besser aus?

    Naja, im objektorientierten Spiel gibt es verdammt viele Sätze. Stets trägt das Verb die meiste Bedeutung. Aber oft ist eines der Objekte der Täter und viel wichtiger als die anderen. Es wird dann Subjekt genannt und vor das Verb geschrieben.
    Also statt

    schlagen(ich,hund,stock);
    

    nun

    ich.schlagen(hund,stock);
    

    Ich kann gut leben mit

    int i;
    i.for(0,100)
      v.push_back(i);
    

    oder

    for(int i=0;i<100;i++)
      v.push_back(i);
    

    Kann ich es auch noch bei

    for(int i=0;i<100;i++)
      push_back(v,i);//Styleguide: Falls eines der Objekte das Subjekt ist, 
        //soll es an erster Stelle stehen
    

    Aber hört es da auf? Oder kommt knivil und macht draus

    int i;
    for(i=0;<(i,100);++(i))
      push_back(v,i);
    

    ?

    Ob die Subjekt-Vorne-Schreibweise so prickelnd sinnvoll ist, weiß ich nicht. Man müßte es mal ausprobieren, immer eine Subjekt-Drin-Schreibweise parallel anzubieten (simpler forwarder zur eigentlichen Methode) und von außen nur den Forwarder nehmen. Ich könnte mir vorstellen, daß das gar nicht mal so unhübsch ist.

    class Boot{
       int foo(double d){
       }
       friend int foo(Boot& b,double d){return b.foo(d);}
    


  • árn[y]ék schrieb:

    Ich persönlich ziehe sqrt(3) eindeutig 3.sqrt vor. Denn die Wurzel ist keine der Zahl 3 eigenen Eigenschaft sondern letztlich eine algebraisch definierte und auf die konkrete Zahl angewandte Funktion. Andernfalls könnte man jeden injektiven Algorithmus als feste Eigenschaft einer jeden einzelnen Zahl auffassen.

    Aber Du spannst zwei Welten auf. Die programmierwelt mit b.sqrt() beo Booten und überhaupt überall, wo viel OO gemacht wird und sqrt(b) wenns mathematisch wird. Das ist von Übel und wird immer Unzufriedenheit und Reibungverluste bringen.



  • Die pure OOPler machen dann:

    ich schlage: hund mit: stock
    


  • Zeus schrieb:

    Die pure OOPler machen dann:

    ich schlage: hund mit: stock
    

    nein, dann lieber

    ich schlage opfer=hund waffe=stock
    

    Lassen wir Smalltalk am besten bei Seite, was konkrete Syntax angeht, und stellen uns OO anhand anderer Sprachen vor, das führt sonst nur zu ärger mit ein paar Smalltalk-Fanboys hier. Konzepte wie Metaklassen kannste natürlich gerne zeigen, das würde ja auch der Ort für seine statics sein.



  • SideWinder schrieb:

    @Zeus: Wir wollen ja static vermeiden und schönes OOP machen.

    DU willst das vielleicht 😉

    IMO ist das nicht-vorhandensein von freien Funktionen eine der schlimmsten Sachen in Java, C# oder ähnlichen Sprachen.
    Wer sagt dass freie Funktionen böse sind, und nicht OOP?

    Das führt doch bloss zu völlig fragwürdigen Hilfsklassen, die nur statische Methoden enthalten, weil freie Funktionen ja pöse sind.
    In C# hat man das ganze sogar so weit getrieben, dass man ganze Klassen explizit als solche fragwürdigen Hilfsklassen deklarieren kann, indem man sie static macht *schauder*. Wo da jetzt noch der grosse Unterschied zu nem Namespace mit freien Funktionen sein soll, entzieht sich mir völlig.
    (OK, eine Klasse kann man soweit ich weiss nicht auf zwei oder mehr Assemblies aufsplitten, nen Namespace sinnvollerweise schon, aber das war's dann glaube ich auch schon)

    Klar könnte man die statischen Methoden auch ganz verbieten, nur würde es vermutlich nicht viel ändern. Sin() etc. würden dann zu nicht-statischen Methoden einer "Math" Klasse, ala so:

    double a = 1.23;
    Math m = new Math();
    double s = m.Sin(a);
    

    Ausser dass der GC mehr zu tun bekommt, ändert sich nix.

    Natürlich könnte man auch die Primitiven Datentypen zu vollwertigen Klassen hochstufen, und ihnen Funktionen wie Sin() etc. verpassen. Bloss... wie soll man dann jemals selbst Funktionen programmieren die ebenfalls nur mit Primitiven Datentypen arbeiten?
    OK, man könnte natürlich etwas machen wie es die Extension-Methods in C# sind, also eine Möglichkeit schaffen Klassen nachträglich zu erweitern.

    Nur... wo, ausser der Schreibweise, ist da dann noch der Unterschied zu freien Funktionen?



  • Ich finde das Erweitern von Klassen eine gute Lösung. Vor allem weil es ja nicht nur Utility-Methoden sind sondern oftmals auch wirkliche Erweiterungen - die dann wenig in einer ausgelagerten Hilfsmethode zu suchen haben (wie ich finde).

    MfG SideWinder



  • Was ist der Unterschied zwischen einer Utility-Methode und einer "wirklichen Erweiterung"?

    Und was machst du mit Methoden die nicht klar einer einzigen Klasse zuzuordnen sind?



  • hustbaer schrieb:

    Was ist der Unterschied zwischen einer Utility-Methode und einer "wirklichen Erweiterung"?

    Und was machst du mit Methoden die nicht klar einer einzigen Klasse zuzuordnen sind?

    Eben weil der Unterschied oftmals nicht ganz klar ist, sollte es vielleicht nur eine Möglichkeit geben.

    Welche Methoden sind nicht klar einer Klasse oder einer Generalisierung dieser zuordenbar?

    MfG SideWinder



  • SideWinder schrieb:

    hustbaer schrieb:

    Was ist der Unterschied zwischen einer Utility-Methode und einer "wirklichen Erweiterung"?

    Und was machst du mit Methoden die nicht klar einer einzigen Klasse zuzuordnen sind?

    Eben weil der Unterschied oftmals nicht ganz klar ist, sollte es vielleicht nur eine Möglichkeit geben.

    Man wird immer eine Unterscheidung brauchen, zumindest solange man die Kapselung nicht aufgeben will. Es wird immer "berechtigte" Funktionen geben müssen, und weitere die eben keine speziellen "Rechte" haben. Natürlich könnten die "berechtigten" Funktionen auch "freie" Funktionen sein.
    Diese Variante würde ich dann entschieden vorziehen.

    Welche Methoden sind nicht klar einer Klasse oder einer Generalisierung dieser zuordenbar?

    atoi, itoa (int vs. string)
    ntohl, htonl (int vs. ... socket? cpu-architecture?)
    multiplikation von vektor mit skalar, vektor mit matrix, matrix mit skalar
    Intersect(Circle, Line)
    ...

    Und nur um dem Vorschlag Geometry.Intersect(Geometry other) zuvorzukommen: was wenn nicht alle NxN Möglichkeiten implementiert sind? Und wieso sollte der User sich damit rumschlagen müssen, den Return-Typ explizit zu casten, wo er doch weiss dass Intersect(Circle, Line) immer nullable<Line> als Ergebnis hat? Nur weil es noch Boxen und Splines und was nicht noch alles gibt, und die Intersection z.B. einer Box mit einem Kreis was ganz anderes als eine Linie sein kann?

    Und was machst du mit Funktionen ala ISNULL(a, b), wo a oder b oder beide NULL sein dürfen? Brauchen wir erst wieder eine fragwürdige Utility Klasse die dann ISNULL implementiert, und von der wir - vollkommen unnötigerweise - erstmal ein Hilfsobjekt erstellen müssen um die ISNULL Funktion überhaupt aufzurufen.



  • SideWinder schrieb:

    Welche sehen denn in der obj.method()-Syntax besser aus?

    Ich habe immer das Gefühl, das obj.method() angebracht ist, wenn das Objekt dadurch verändert wird. Im Umkehrschluss benutze ich also function(obj) immer dann, wenn obj durch die Operation nicht verändert wird. Ist etwas ähnliches wie das, was volkard als "Subjektschreibweise" meint. Wird das Objekt verändert->Subjekt.

    das passt auch ziemlich gut vom allgemeinen Gefühl:

    simulation.start();
    vs
    start(simulation);
    

    Ich habe das Gefühl, dass ich beim ersten wesentlich schneller kapiere, was Sache ist als beim zweiten. Nebenbei ist dabei simulation.start() auch eine Imperativ-Konstruktion: "los veränder dich". Noch ein Grund dafür, besonders rausgehoben zu werden.



  • Wenn ihr es unbedingt OO wollt, dann last Sinus doch weiterhin eine Funktion sein.

    class SinusFunction( RadOrDeg )
    {
        INumber calculate( INumber )
        Collection<INumber> calculate( Collection<INumber> )
    }
    

    Oder mit Operatorüberladung, wenn man calculate nicht will.

    Und wenn man nen wirklich guten Compiler hat, könnte der den ganzen OVerhead den man jetzt so schön dazu prpgrammiert hat wieder weg optimieren.


Anmelden zum Antworten