Guter Stil oder Overhead?
-
Meines Wissens kannst du auch dann einfach schreiben:
enum CardValues { Ace = 1, Two, // zählt automatisch weiter // usw }
EDIT:
Warum würdest du '==' global definieren?
meinst du public / öffentlich? Du kannst ihn (den Operator) sonst doch gar nicht außerhalb verwenden (?)
-
Nexus schrieb:
operator==
würde ich als globale Funktion implementieren (wie folgt), ausserdem wäre einoperator!=
als Gegenstück vielleicht sinnvoll.bool operator==(const Card& lhs, const Card& rhs);
ich bezog mich auf diese Aussage mit "Global".
-
Weil man Operationen auf Objekte nur dann als Memberfunktion definieren sollte, wenn sie etwas mit der Kapselung der Klasse zu tun haben. Wenn m_Symbol und m_Value also von außen zugänglich sind (z.B. durch getter Funktionen), dann mach den Vergleich zu einer freien Funktion. Sonst lass es so.
-
cooky451 schrieb:
Weil man Operationen auf Objekte nur dann als Memberfunktion definieren sollte, wenn sie etwas mit der Kapselung der Klasse zu tun haben. Wenn m_Symbol und m_Value also von außen zugänglich sind (z.B. durch getter Funktionen), dann mach den Vergleich zu einer freien Funktion. Sonst lass es so.
Das ist immer noch keine Begründung, sondern nur die Aussage, dass man es so machen soll.
-
hmmmm schrieb:
Das ist immer noch keine Begründung, sondern nur die Aussage, dass man es so machen soll.
Das stimmt. Und die Begründung ist auch nicht völlig unkontrovers, aber es ist recht akzeptierter C++ Stil. Die Kapselung der Klasse wird meiner Meinung verbessert, je sauberer (und kleiner) das Interface ist, und desdo weniger Schnittstellen es somit nach außen gibt, auf die man achten muss. Ist hier natürlich trivial, aber ich ziehe es generell vor.
Falls dich das nicht überzeugt, google mal nach "prefer non-member non-friend functions". Wenn dich das immer noch nicht überzeugt, mach's halt anders, aber behalte im Hinterkopf, dass es recht viele Leute überzeugt hat.
-
cooky451 schrieb:
hmmmm schrieb:
Das ist immer noch keine Begründung, sondern nur die Aussage, dass man es so machen soll.
Das stimmt. Und die Begründung ist auch nicht völlig unkontrovers, aber es ist recht akzeptierter C++ Stil. Die Kapselung der Klasse wird meiner Meinung verbessert, je sauberer (und kleiner) das Interface ist, und desdo weniger Schnittstellen es somit nach außen gibt, auf die man achten muss. Ist hier natürlich trivial, aber ich ziehe es generell vor.
Falls dich das nicht überzeugt, google mal nach "prefer non-member non-friend functions". Wenn dich das immer noch nicht überzeugt, mach's halt anders, aber behalte im Hinterkopf, dass es recht viele Leute überzeugt hat.Kenne ich und hat mich noch nie überzeugt. Interessant ist auch, dass es nie einer wirklich gut begründen kann. Es kommen immer nur so Aussagen, wie google mal ... lies mal den Blog...
-
Eigentlich ist die Sache klar. Es ist eine der wichtigsten Erkenntnisse der Softwaretechnik, dass man die Abhängigkeiten zwischen Komponenten so weit wie möglich verringern sollte (lose Kopplung) und die Komponenten in sich kompakt gehalten werden sollen (hohe Kohäsion).
Und genau das wird u.A. durch ein schlankes Interface erreicht.
-
JFB schrieb:
Eigentlich ist die Sache klar. Es ist eine der wichtigsten Erkenntnisse der Softwaretechnik, dass man die Abhängigkeiten zwischen Komponenten so weit wie möglich verringern sollte (lose Kopplung) und die Komponenten in sich kompakt gehalten werden sollen (hohe Kohäsion).
Ja.
Und genau das wird u.A. durch ein schlankes Interface erreicht.
Und das ist wieder keine Begründung, warum alles mögliche in freie Funktionen stecken dazu führen sollte, sondern nur die Behauptung, dass es so wäre.
Warum erhöht es die Abhängigkeit zwischen Komponenten, wenn ich eine toUpper-Methode in eine String-Klasse stecke? Warum sollte ich weniger Abhängigkeiten haben, wenn ich eine freie Funktion toUpper(string) habe?
-
Angenommen du nutzt eine Klasse die nicht von dir geschrieben ist, oder eine Klasse die anderweitig ein festes Interface hat und nicht verändert werden sollte. (z.B. weil sie bereits getestet ist etc.)
Wenn du sie jetzt erweiterst, änderst du ihr Interface, musst sie neu testen etc. Mit einer freien Funktion musst du das nicht. Insbesondere wenn es eh nicht dein Code ist, wirst du eine freie Funktion vorziehen. Wenn du das vorher aber nicht gemacht hast, wird es nicht mehr einheitlich.
-
Warum erhöht es die Abhängigkeit zwischen Komponenten, wenn ich eine toUpper-Methode in eine String-Klasse stecke? Warum sollte ich weniger Abhängigkeiten haben, wenn ich eine freie Funktion toUpper(string) habe?
Das geht am Punkt vorbei, toUpper ist nichts, was ich persönlich aus den von mir genannten Erwägungen in eine eigene Komponente verladen würde.
Abgesehen davon sind freie Funktionen im OOA eh nicht wirklich präsent.Aber unabhängig von der Operatorüberladung, im Allgemeinen ist das eine Gratwanderung:
Auf der einen Seite ist das "Minimalinterface", eine ausgesprochen hässliche Lösung: Getter und Setter für alle Interna, der Rest wird über "Manipulator"-Komponenten gelöst. Das ist Quatsch und trägt nichts zur Auflösung von Abhängigkeiten bei, sondern stellt eben gerade neue (zwischen Komponente und Manipulator) her (hohe Kohäsion innerhalb der Komponenten, aber leider auch starke Kopplung).
Auf der anderen dagegen das Monolith-Antipattern: Eine Komponente ist für alle Operationen verantwortlich, die mit ihr zu tun haben (sehr niedrige Kohäsion, dafür kaum Kopplung). Man muss nur in den richtigen Momenten den richtigen Weg wählen, um beide Maße an ihr Optimum anzunähern.Ein Beispiel: Das Visitor-pattern. Im simplen Fall von Binärbaumtraversierungen wird die Motivation schon sichtbar: Es ist nicht Aufgabe eines Binärbaums, sich selbst Pre-, Post- oder Inorder zu durchlaufen. Das ist Logik, die man außerhalb der Datenverwaltung (dem Baum) unterbringen möchte. Das erhöht die Kopplung zwar, verringert aber auch die Kohäsion. Ob und wie sehr sich das lohnt muss man im Einzellfall entscheiden.
-
JFB schrieb:
Warum erhöht es die Abhängigkeit zwischen Komponenten, wenn ich eine toUpper-Methode in eine String-Klasse stecke? Warum sollte ich weniger Abhängigkeiten haben, wenn ich eine freie Funktion toUpper(string) habe?
Das geht am Punkt vorbei, toUpper ist nichts, was ich persönlich aus den von mir genannten Erwägungen in eine eigene Komponente verladen würde.
Abgesehen davon sind freie Funktionen im OOA eh nicht wirklich präsent.Naja, das war der Punkt, aber sonst kann ich dir zustimmen.
-
Im Falle der operatoren ist nicht nur Kapselung sondern auch Symmetrie ein Thema.
Ista == b
etwas komplett anderes alsb == a
? Nicht, oder? Beide sind gleichwertig, deshalb sollte der operator== dies auch ausdrücken, und nicht an irgend ein einzelanes Objekt gebunden sein.
-
hmmmm schrieb:
Warum erhöht es die Abhängigkeit zwischen Komponenten, wenn ich eine toUpper-Methode in eine String-Klasse stecke? Warum sollte ich weniger Abhängigkeiten haben, wenn ich eine freie Funktion toUpper(string) habe?
Du hast
toUpper
von der Stringklasse entkoppelt und kannst Spezialisierungen fuer andere Stringklassen anbieten, ja selbst fuerstd::vector<char>
std::array<char, 15>
oderchar
-Arrays, die selbst keine Klassen sind. Und mit Templates musst dutoUpper
wahrscheinlich nur einmal implementieren.
-
Etwas ausführlicher:
Dr Dobbs: How Non-Member Functions Improve Encapsulation
-
knivil schrieb:
hmmmm schrieb:
Warum erhöht es die Abhängigkeit zwischen Komponenten, wenn ich eine toUpper-Methode in eine String-Klasse stecke? Warum sollte ich weniger Abhängigkeiten haben, wenn ich eine freie Funktion toUpper(string) habe?
Du hast
toUpper
von der Stringklasse entkoppelt und kannst Spezialisierungen fuer andere Stringklassen anbieten, ja selbst fuerstd::vector<char>
std::array<char, 15>
oderchar
-Arrays, die selbst keine Klassen sind. Und mit Templates musst dutoUpper
wahrscheinlich nur einmal implementieren.Wie frage ich
std::vector<char>
nochmal nach seinem Encoding?
-
Wie fragst du denn std::string?
-
Ja, toUpper funktioniert hier nur, wenn man Information-Hidding verletzt und annimmt, man weiß wie der String seine Daten intern hält.
-
Irgendwie verstehe ich nicht, worauf du gerade hinaus willst.
-
hmmmm schrieb:
Wie frage ich
std::vector<char>
nochmal nach seinem Encoding?Gar nicht. Die Unterstuetzung fuer Utf in C++ ist nicht besonders gut. Aber deswegen kannst du trotzdem eine Klasse
UtfString
definieren undtoUpper
dafuer spezialisieren, die das Encoding abfragt.Ja, toUpper funktioniert hier nur, wenn man Information-Hidding verletzt und annimmt, man weiß wie der String seine Daten intern hält
Wie du siehst, kann das am Typ festgemacht werden. Und warum sollte ich den verstecken?
-
ist glaub ich sinnlos weietr zu machen.