About a code. Code Style Frage.



  • TyRoXx schrieb:

    hustbaer schrieb:

    ...

    Versuch doch mal nachzuvollziehen was ich schreibe und warum. Ich mache das nicht, um jemanden zu ärgern. Dass dir etwas nicht passt, heißt nicht automatisch, dass es falsch ist.

    Das kann ich dir 1:1 zurückgeben.



  • Jockelx schrieb:

    Schreibst du für jede Funktion, die oft mit den gleichen Parametern aufgerufen wird, eine Wrapper-Funktion?

    Ja, das nennt man "strukturierte Programmierung".

    Jockelx schrieb:

    Gibt es bei dir auch immer nur einen Konstruktor, der alle vorhandenen Werte durch den Client setzen lässt?

    Immer würde ich nicht sagen. Aber meine Klassen haben neben Standard-, Kopier- und Move-Konstruktur meist nur einen weiteren Konstruktor. Ich habe schon lange genug erklärt, warum der Konstruktor dann fast nie Standardparameterwerte bekommt.

    Jockelx schrieb:

    Stelle ich mir schlimm vor, wenn ich für jeden Parameter immer erst rumsuchen müsste, wie ich da den Default herkriege.

    Und ich stelle es mir schlimm vor, wenn ich bei jedem Aufruf rumsuchen muss, um alle übergebenen Werte herauszufinden.

    hustbaer schrieb:

    Um zu entscheiden was besser ist - verbose oder concise - kommt es mMn. sehr darauf an wie oft man ein bestimmtes Konstrukt in einem Programm findet.
    Wenn man es 3x in einem Programm findet, dann definitiv verbose.
    Wenn man es 3000x findet, dann definitiv concise.

    Ich finde nicht, dass die Häufigkeit der Aufrufe relevant sein sollte. Wenn ich die Funktion deklariere, weiß ich doch gar nicht, wie oft die am Ende tatsächlich benutzt wird. Die Ersparnis an Tipperei ist also rein spekulativ und an sich IMO schon fraglich. Weniger Tipperei und weniger Code machen nicht automatisch produktiv. Wenn das so wäre, müssten die schnellsten Entwickler LZMA-kodierte Maschinensprache binär eingeben. Viel wichtiger ist die Lesbarkeit von Code. Deswegen benutzen wir Hochsprachen und deswegen sollten alle Funktionsargumente ersichtlich sein, ohne die Deklaration nachzuschlagen.

    hustbaer schrieb:

    Dann muss sich "der neue" halt eine Sache mehr merken bevor er richtig produktiv mittun kann, dafür müssen "die alten" nicht an 3000 Stellen den "noise" mitlesen den sie eh schon kennen.

    Wenn ich 1000 Funktionen habe, die jeweils 1000 mal im Code aufgerufen werden, habe ich gar nichts von Standardparameterwerten. Dann müsste ich nämlich sowieso jedes Mal die Werte nachschlagen.



  • TyRoXx schrieb:

    Jockelx schrieb:

    Stelle ich mir schlimm vor, wenn ich für jeden Parameter immer erst rumsuchen müsste, wie ich da den Default herkriege.

    Und ich stelle es mir schlimm vor, wenn ich bei jedem Aufruf rumsuchen muss, um alle übergebenen Werte herauszufinden.

    Also ich mach gerade etwas SFML. Will da ein Grundgerüst bauen und mach erstmal ein Fenster auf:

    sf::Window window(sf::VideoMode(800, 600), "SFML window");
    

    Super! So will ich das haben.
    Hättest du das designed, dann sähe der Ctor nicht so

    sf::Window::Window(VideoMode mode, const String& title, Uint32 style = Style::Default,	const ContextSettings& 	settings = ContextSettings())
    

    aus, sondern ich muss mich durch Sachen durchwurschteln, die mich erstmal überhaupt nicht interessieren.
    Wenn mein Fenster nicht das macht was es soll, dann kann ich immer noch nachschauen wo Styles definert werden und woher man einen Context kriegt und wozu der gut ist.
    Aber ich glaube unsere Positionen werden sich durch die Diskussion eh nicht ändern.



  • Das geht wieder in Richtung Konvention. Man weiß, wie auf seinem Betriebssystem ein typisches Fenster aussieht. Bei dem Aufruf kann man also davon ausgehen, dass das entstehende Fenster so aussehen wird.

    Ich hätte aber auch nichts dagegen das Kind beim Namen zu nennen:

    sf::Window CreateDefaultWindow(VideoMode mode, const String& title);
    sf::Window CreateCustomWindow(VideoMode mode, const String& title, Uint32 style, const ContextSettings& settings);
    

    Standardparameterwerte des Konstruktors zu nutzen ist eben die "clevere" Variante, wobei "clever" nicht immer gut ist in der Programmierung. Kann ich als Anfänger erkennen, dass es weitere Parameter gibt, ohne die Deklaration nachzuschlagen? Bequemlichkeit hat eben auch Nachteile.
    Am Namen CreateDefaultWindow kann ich hingegen erkennen, dass es höchstwahrscheinlich andere Funktionen zum Erstellen von Fenstern gibt.



  • Ist eigentlich keine wirklich relevante Diskussion, aber ich muss da doch mal meinen Senf abgeben.

    Es gibt in C++ die Möglichkeit, default-Werte für Parameter anzugeben. Man kann sicher dieses Feature missbrauchen, wie jedes andere Feature auch. Dennoch ist es sehr nützlich, wenn man es geeignet einsetzt.

    Ich finde das Beispiel der Rundungsfunktion auf 2 Nachkommastellen eher unglücklich. 2 Nachkommastellen erscheinen mir nicht überall eine natürliche Wahl zu sein.

    Aber der Kollege TyRoXx hat bereits die LZMA-Komprimierung erwähnt. Die ist parametrisierbar. Ich kann alles mögliche einstellen. In der Regel reicht es aber, mit geeigneten Standardwerten zu arbeiten. Daher würde ich eine Klasse, die LZMA-Komprimierung macht, eben mit diesen Standardwerten ausstatten (ich habe übrigens tatächlich mal genau das gemacht, also eine LZMA-Komprimierklasse).

    Oder ich denke da an Netzwerkklassen. Wenn ich einen gepufferten Stream habe, dann ist in aller Regel eine Puffergröße von 8k für Netzwerkkommunikation ein geeigneter Wert. Sehr selten macht es Sinn, davon abzuweichen. Daher würde ich (und habe ich wieder mal 😃 ) einen Standardwert für die Puffergrösse von 8k angegeben.

    Das ganze macht den Code lesbarer, da ich nicht bei jedem Methodenaufruf jedes Detail angeben muss, sondern nur die relevanten Parameter.

    Sicher kann man das auch ohne Default-Parameter lösen. Aber dafür haben wir eine Hochsprache, die abstrahiert. Ich kann ja auch in C objektorientiert programmieren, nur ist das in C++ doch deutlich einfacher, da mir hier deutlich mehr Ausdrucksmittel zur Verfügung stehen.



  • Du meinst wohl

    sf::Window CreateDefaultWindow(VideoMode mode, const String& title);
    sf::Window CreateCustomWindow(VideoMode mode, const String& title, Uint32 style);
    sf::Window CreateCustomWindowWithContext(VideoMode mode, const String& title, Uint32 style, const ContextSettings& settings);
    

    Gut, dass es nicht noch einen Parameter mehr hat.
    Spätestens, dann wirds unleserlich

    sf::Window CreateCustomWindowWithContextAndOther(VideoMode mode, const String& title, Uint32 style, const ContextSettings& settings, OtherT& other);
    

    Wobei ich zugeben muss, dass mir nach der Diskussion heute, bei einer Java ArrayList insertAt(index, bla) auch besser gefallen würde, als das tatsächliche
    add(bla) und add(index, bla).



  • tntnet schrieb:

    Oder ich denke da an Netzwerkklassen. Wenn ich einen gepufferten Stream habe, dann ist in aller Regel eine Puffergröße von 8k für Netzwerkkommunikation ein geeigneter Wert. Sehr selten macht es Sinn, davon abzuweichen. Daher würde ich (und habe ich wieder mal 😃 ) einen Standardwert für die Puffergrösse von 8k angegeben.

    Auch hier halte ich es nicht für sinnvoll mehrere Ebenen zu vermischen. Die "Netzwerkklasse" weiß doch gar nichts über meinen tatsächlichen Anwendungsfall. Warum soll die sich irgendeinen Standardpuffer aus den Fingern saugen? Ich weiß doch erst bei der Benutzung der Klasse, was ich damit machen will. Dann kann ich immer noch als "Standardwert" eine Konstante von 8k definieren, dokumentieren und da übergeben. Die Klasse kann auch eine Konstante namens RecommendedBufferSize oder so beinhalten. Wenn ich die verwende, dokumentiert das für jeden Leser, dass ich mich bewusst für einen aus den Fingern gesaugten Wert entschieden habe.

    tntnet schrieb:

    Das ganze macht den Code lesbarer, da ich nicht bei jedem Methodenaufruf jedes Detail angeben muss, sondern nur die relevanten Parameter.

    Eigentlich macht es den Code weniger lesbar, weil ich nach den Werten herumsuchen muss, die mich interessieren. Was du meinst, ist leichter schreibbar ohne nachzudenken.

    tntnet schrieb:

    Sicher kann man das auch ohne Default-Parameter lösen. Aber dafür haben wir eine Hochsprache, die abstrahiert.

    Default-Parameter sind keine Abstraktion. Eine explizite Konstante auf einer höheren Ebene, wie ich sie vorgeschlagen habe, wäre vielleicht Abstraktion.

    Jockelx schrieb:

    sf::Window CreateDefaultWindow(VideoMode mode, const String& title);
    sf::Window CreateCustomWindow(VideoMode mode, const String& title, Uint32 style);
    sf::Window CreateCustomWindowWithContext(VideoMode mode, const String& title, Uint32 style, const ContextSettings& settings);
    

    So etwas befürworte ich natürlich nicht. Dass es überhaupt eine Default-Variante gibt, kommt nicht einmal von mir, sondern du wolltest so etwas haben. Eine Anfänger- und eine Expertenfunktion sind doch gar nicht so schlecht. Wenn dann mal ein Parameter zur Expertenfunktion dazukommen soll, kann man in Gottes Namen einen Standardparameterwert benutzen, um das Interface kompatibel zu halten. Das ist ein Grund, den ich verstehen kann. Reine Bequemlichkeit auf Kosten der Nachvollziehbarkeit kann ich aber nicht als Grund verstehen.

    Wäre das hier so schlimm? Es wäre zumindest leichter verständlich und lesbarer als so ein Bitset.

    sf::Window CreateWindow(
    	VideoMode mode,
    	const String& title,
    	TitleBarStyle titleBar,
    	CloseStyle closing,
    	ResizeStyle resizing,
    	ScreenSection section,
    	const ContextSettings &settings
    );
    
    sf::Window window = sf::CreateWindow(
    	sf::VideoMode(800, 600),
    	"SFML window",
    	sf::TitleBarStyle::Visible, //oder sf::TitleBarStyle::Invisible
    	sf::CloseStyle::Closable, //oder sf::CloseStyle::NotClosable
    	sf::ResizeStyle::Resizable, //oder sf::ResizeStyle::FixedSize
    	sf::ScreenSection::Frame, //oder sf::ScreenSection::Fullscreen
    	sf::ContextSettings()
    );
    

    Ob man das obige oder das folgende aus dem Tutorial kopiert und einfügt, ist doch egal. Das obere hat noch den Vorteil, dass es selbsterklärend ist. Mit ContextSettings habe ich mich jetzt nicht auseinandergesetzt.

    sf::Window window(sf::VideoMode(800, 600), "SFML window");
    


  • TyRoXx schrieb:

    Ob man das obige oder das folgende aus dem Tutorial kopiert und einfügt, ist doch egal. Das obere hat noch den Vorteil, dass es selbsterklärend ist. Mit ContextSettings habe ich mich jetzt nicht auseinandergesetzt.

    Aber der Konstruktor von ContextSettings hat auch wieder default Werte (sogar 5). Von daher seh ich jetzt nicht den Vorteil dafür gleich direkt einen default Wert zu verwenden. Ansonsten musst du ja schreiben

    sf::Window window = sf::CreateWindow(
        sf::VideoMode(800, 600),
        "SFML window",
        sf::TitleBarStyle::Visible, //oder sf::TitleBarStyle::Invisible
        sf::CloseStyle::Closable, //oder sf::CloseStyle::NotClosable
        sf::ResizeStyle::Resizable, //oder sf::ResizeStyle::FixedSize
        sf::ScreenSection::Frame, //oder sf::ScreenSection::Fullscreen
        sf::ContextSettings(0, 0, 0, 2, 0)
    );
    

    wobei das dann ja wieder gar nichts erklärt, also noch zusätzliche konstanten Deklarationen braucht. Jeden Parameter in eine neue Zeile schreiben wäre hier auch eher unübersichtlich. Ganz schön viel Arbeit (und Code) für was eigentlich ganz einfaches, finde ich...



  • So vielleicht?

    sf::Window window = sf::CreateWindow(
        sf::VideoMode(800, 600),
        "SFML window",
        sf::TitleBarStyle::Visible,
        sf::CloseStyle::Closable,
        sf::ResizeStyle::Resizable,
        sf::ScreenSection::Frame,
        sf::ContextSettings::OpenGL_2_0_WithoutExtras
    );
    


  • Könnte man machen, aber ich finde

    Window w(VideoMode(800, 600), "SFML window");
    

    hat halt auch was.

    Ich hab mir deine Argumente durchgelesen und kann diese nachvollziehen (ist ja letztendlich eh alles Geschmacksache).

    Aber mMn muss man sich ja eh immer die Deklaration anschauen bevor man (irgend)eine Funktion benutzt. Allein schon damit man weiß ob gewisse Parameter (const) Referenzen sind oder nicht (kann man ja am Aufruf nicht erkennen). Nur weil es jemand falsch machen könnte der keine Ahnung hat und sich nichtmal die Deklaration der Funktionen anschaut die er benutzt finde ich ein bisschen übervorsichtig 😃

    Deswegen seh ich das mit default Parametern nicht so dramatisch, mir ist übersichtlicher Code wichtiger als alles super explizit hinzuschreiben. Das lenkt dann nur vom wesentlichen ab.



  • happystudent schrieb:

    Aber mMn muss man sich ja eh immer die Deklaration anschauen bevor man (irgend)eine Funktion benutzt. Allein schon damit man weiß ob gewisse Parameter (const) Referenzen sind oder nicht (kann man ja am Aufruf nicht erkennen).

    Beim Benutzen ja. Bei existierendem Code (wie dem SFML-Tutorial) nicht. Aus dem Aufruf kann man fast alles ableiten, was zum Verständnis notwendig ist. Nicht-const-Referenzen können das in meinem Beispiel nicht sein, weil das gar nicht übersetzbar wäre - unter der berechtigten Annahme, dass da keine globalen Variablen übergeben werden.

    happystudent schrieb:

    Deswegen seh ich das mit default Parametern nicht so dramatisch, mir ist übersichtlicher Code wichtiger als alles super explizit hinzuschreiben. Das lenkt dann nur vom wesentlichen ab.

    Alle Argumente sind für mich wesentlich, unabhängig von subjektiven Defaults, die andere gewählt haben. Wenn ein Argument unwesentlich ist, kann man auch einfach den Parameter löschen.

    happystudent schrieb:

    Nur weil es jemand falsch machen könnte der keine Ahnung hat und sich nichtmal die Deklaration der Funktionen anschaut die er benutzt finde ich ein bisschen übervorsichtig 😃

    Code, für dessen Verständnis man weniger nachschlagen muss, ist tendentiell besser, oder nicht?



  • TyRoXx schrieb:

    Wenn ein Argument unwesentlich ist, kann man auch einfach den Parameter löschen.

    Vielleicht nicht unwesentlich, aber "weniger" wesentlich.

    Ich finde das halt gerade für Anfänger eine schöne Sache. In Matlab zum Beispiel ist alles voll von default Werten. Das macht es halt einfach extrem Einsteiger- und Benutzerfreundlich, weil ich einfach (zum Beispiel) einem Solver eine Kostenfunktion übergeben kann und in 99% der Fälle sind die default Einstellungen absolut ausreichend um das Problem zu lösen. Falls doch nicht, kann ich immer noch tiefer in die Materie einsteigen.

    TyRoXx schrieb:

    Alle Argumente sind für mich wesentlich, unabhängig von subjektiven Defaults, die andere gewählt haben.

    Im Gegenteil, meiner Meinung nach.

    Ich finde es sogar wertvoll und gut wenn mir von einem Experten, der sich ausführlich mit dem jeweiligen Problem auseinandergesetzt hat, ein vermutlich sinnvoller Wert vorgeschlagen wird.

    Das ist auch eine Art der Dokumentation und zusätzliche Information/Mehrwert der oft auf viel Erfahrung seitens des Entwicklers basiert. Gerade bei irgendwelchen (nicht direkt ersichtlichen) numerischen Konfigurationsparametern. Ignorieren kann ich den Wert ja immer noch.



  • TyRoXx schrieb:

    happystudent schrieb:

    Nur weil es jemand falsch machen könnte der keine Ahnung hat und sich nichtmal die Deklaration der Funktionen anschaut die er benutzt finde ich ein bisschen übervorsichtig 😃

    Code, für dessen Verständnis man weniger nachschlagen muss, ist tendentiell besser, oder nicht?

    Nein, nicht immer.
    Was ich auch schon geschrieben habe, was du einfach ignoriert hast.
    Weil du mich lieber über Dinge "belehrst" (wie z.B. schlechtere Performance bei optional<string>), um die es hier überhaupt nicht geht.
    Was mir genau so auf den Sack geht wie (EDIT: dich) der "Ton" den ich dann anschlage wenn mir jemand auf den Sack geht.

    Verstehst du jetzt vielleicht warum ich hier (ganz klar, zugegeben!) "unsachlich" reagiert habe?

    Und was Default-Werte angeht (allgemein, unabhängig davon ob es Parameter sind oder Properties oder was auch immer): dass die extrem wichtig sind sollte klar sein.
    Man muss sich bloss mal angucken wie viele Properties ein durchschnittliches Widget eines modernen GUI Frameworks hat (z.B. WCF). Klar, für die meisten dieser Properties gibt es keine Ctor-Parameter, aber Default-Werte haben sie dennoch, und ohne solche Default-Werte wären die entsprechenden GUI Frameworks unbedienbar.
    Genau so sieht es bei Web-Service Stacks aus bzw. allgemein einfach vielen Libraries die flexibel einsetzbar sind.

    Und zu Funktions-Parametern besteht da kein wesentlicher Unterschied.

    Bei Sprachen die optionale Named-Parameters unterstützen sind Default-Werte für Parameter mMn. wirklich sinnlos. Lieber nen optionalen Parameter machen -- dann muss der Default-Wert nicht bei der Übersetzung bekannt sein.
    Bei Sprachen die keine Named-Parameters haben sind Default-Werte für Parameter aber einfach ein nettes convenience Feature, das sowohl dem Library-Programmer als auch dem User-Programmer Arbeit abnimmt.

    Was die erwähnte Netzwerk-Puffergrösse angeht: den User-Programmer interessiert das normalerweise nicht, und es soll ihn auch nicht interessieren. Er soll den Wert gar nicht angeben, so lange er nicht genau weiss welcher Wert da im Allgemeinen (oder speziell für ihn) Sinn macht. Und ohne sich ernsthaft mit der Materie auseinanderzusetzen kann er das nie so gut wissen wie der Library-Programmer. Der sich hoffentlich etwas damit beschäftigt hat, und nicht einfach eine willkürliche Zahl hingeschrieben hat.
    Mein einziger Kritikpunkt wäre dabei: ich würde nicht fix 8000 angeben, sondern einen "null" Wert als Default übergeben, der für "nimm das was Sinn macht" steht. Also z.B. -1 , boost::none oder null - was auch immer in der jeweiligen Sprache zur Verfügung steht.

    Weil man nicht ausschliessen kann dass für verschiedene Systeme verschiedene Default-Werte sinnvoll sind.

    => Den User-Programmer zu zwingen alle möglichen Parameter anzugeben, von denen er vermutlich die Hälfte nicht versteht/nicht einschätzen kann, verbessert gar nix. Sondern schadet wohl meistens nur.
    Und ein relativ einfaches Mittel um zu ermöglichen dass nicht alles angegeben werden muss, sind eben Default-Werte für Parameter.

    Lieber wären mir Named-Parameters, aber so lange es die nicht gibt, verwende ich gern Default-Werte.

    ps: Ein grosser Vorteil von Default-Werten für Parameter wie sie C++ umsetzt: man muss nix dokumentieren, da man den Wert jederzeit im Header-File nachsehen kann. Der Nachteil davon ist klar (Default-Wert wird bei der übersetzung ins Client-Programm reincompiliert). Der Nachteil von "Anfänger-Funktionen" (egal ob Overload oder mit "eigenem" Namen) ist aber: wenn man keine Doku dazu schreibt, und der User-Programmer den Source nicht vorliegen hat, dann hat er keine einfache Möglichkeit rauszubekommen was die "Anfänger-Funktionen" für Werte verwenden. Und das ist jetzt nicht dahererfunden - die (ansonsten sehr gute!) MSDN Doku enthält etliche Einträge zu solchen "Anfänger-Funktionen", wo nicht dabeisteht was für Werte für die Parameter die "sie nicht haben" verwendet werden.
    (Und auch davon abgesehen finde ich es viel mühsamer in der Doku nachzuschlagen als einfach zu gucken was der Intellisense mir anzeigt bzw. Alt+G zu drücken um zur Deklaration im Header-File zu springen. Speziell bei C++ wo es nicht üblich ist dass Doku mitgeliefert wird die ins Studio mit eingebunden wird, so dass es die Doku zur Funktion/Klasse beim Drücken von F1 findet und anzeigt.)



  • TyRoXx schrieb:

    tntnet schrieb:

    Das ganze macht den Code lesbarer, da ich nicht bei jedem Methodenaufruf jedes Detail angeben muss, sondern nur die relevanten Parameter.

    Eigentlich macht es den Code weniger lesbar, weil ich nach den Werten herumsuchen muss, die mich interessieren. Was du meinst, ist leichter schreibbar ohne nachzudenken.

    Es geht doch darum, Werte nicht explizit erwähnen zu müssen, die eben nicht interessieren. Es sind Werte, die für den Verständnis des Codes eben nicht wichtig sind. Je mehr Informationen - und sei es "DefaultBufferSize" - vorhanden ist, desto mehr wird der eigentliche Sinn versteckt.

    happystudent hat auch ein sehr gutes Beispiel. Ein Fenster in einer GUI Applikation ist ein Fenster. Und wenn ich ein Fenster öffne, welches nichts besonderes hat, dann möchte ich einfach sagen: "öffne ein Fenster" und nicht: "öffne ein Fenster wobei der Parameter A Standard sein soll und Parameter B auch und C erst recht und D wählst Du auch wie immer und E ist eigentlich hier sogar völlig egal". Wenn ich in dem Fenster noch einen Button platziere, wobei der Rahmen Standard sein soll und die Hintergrundfarbe auch und der Font soll das sein wie immer und ich will auch keine besonderen Attribute wie z. B. "deaktiviert" angeben, dann wird das richtig unübersichtlich. In dem ganzen Wust dann noch zu erkennen, dass eigentlich nur ein Fenster mit einem Button geöffnet wird, ist schon nicht mehr so einfach.


Anmelden zum Antworten