OOP: soll man jetzt alles in ne Klasse packen oder watt?
-
Gregor schrieb:
Ach so... dann erweiterst Du bei Dir also den Namespace std für Deine eigenen Projekte? Und andere Bibliotheken, die Du nutzt... machen die das auch?
dann interpretiert er std::sqrt so, daß dies der name für's radizieren ist. und den darf er natürlich bei völliger bedeutungsbeibehaltung duch zuatzimplemetierung erweitern. man darf ja auch dort lebende templates spezialisieren oder von dort lebenden klassen erben.
Ich bin der Meinung, dass wenn Du eigene Typen hast, für die Du eine schon vorhandene Funktion überladen musst, Du auch einen eigenen "Namespace" dafür machen solltest.
warum?
Wo kommt man denn da hin, wenn sich jeder seinen eigenen Namespace std "definiert". Ein Wartungsalptraum. Wenn ein anderer Deinen Code liest, wird er ganz schön lange nach der Definition dieser Funktionen suchen.
ja. rechtsklick auf den funktionsnamen und aussuchen des kontextmenupunks "go to definition". ewig lange zwei sekunden später hat er sie. welche geschwindigkeiten bist du mit eclipse so gewohnt, daß du 2 sekunden (inklusive zweimal klicken) für ganz schön lang halten tust?
-
volkard hat das meiste schon gesagt, aber ich will noch etwas hinzufuegen:
natuerlich fuege ich nicht nach belieben funktionen in std hinzu. Das waere natuerlich bloedsinn.
Aber man muss das jetzt etwas aus einem objekt orientierten aspekt sehen: ich stelle ein interface fuer bestimmte operationen zur verfuegung: zB habe ich sqrt, copy, ...
sqrt zB ist nichts anderes als eine operation auf einem numerischen objekt. ob das objekt jetzt ein int, double oder MyInteger ist, ist dem interface dabei egal.
Wenn ich nun eine klasse MyInteger habe, dann wuerde ich gerne diese Klasse sich wie ein int verhalten lassen. Ich will also quasi das Math-Interface implementieren. Deshalb spezialisiere ich (oder ueberlade - schiess mich tot, keine ahnung was von beiden ich da am besten mache (zuviel java in letzter zeit ;))) sqrt fuer MyInteger.
Oder eben copy, find, etc. fuer container - wenn ich einen eigenen container geschrieben habe, will ich ja dass er auch dieses interface implementiert.
das mache ich natuerlich nicht dauernd und es waere falsch zu sagen man braucht dieses feature, denn von einem technologischem standpunkt aus tut es ein a.sqrt() genauso - aber es sind eben diese features die den code schoener gestalten.
-
volkard schrieb:
Ich bin der Meinung, dass wenn Du eigene Typen hast, für die Du eine schon vorhandene Funktion überladen musst, Du auch einen eigenen "Namespace" dafür machen solltest.
warum?
Hab ich doch gesagt: Ich halte das nicht für besonders wartbar. Abgesehen davon könnte - sogar bei C++ - die Standardbibliothek auf lange Sicht offiziell erweitert werden. Dann kommt vielleicht eine Funktion mit gleicher Signatur in std rein, weil es sich gezeigt hat, dass sich eben jeder so eine Funktion selbst schreibt, weil sie gebarucht wird. Ok... dann gibt es also 2 Funktionen mit gleicher Signatur. Löscht Du dann Deine Funktion? Dann hat die Funktion aus dem offiziellen std aber vielleicht eine ganz leicht andere Semantik und plötzlich funktioniert Dein Code nicht mehr, ist also buggy. (um mal ein Negativ-Beispiel für eine entsprechende Erweiterung zu konstruieren) ...ok, bei EIGENEN Typen, die irgendwo in der Signatur auftauchen, ist das natürlich äußerst unwahrscheinlich. Aber es geht ja generell um die Erweiterungsfähigkeit des Namespaces.
Ich bin halt der Meinung, dass std vom Standard her vorgegeben ist. Wenn man da etwas reinbringen will, soll man bei der Standardisierung mitarbeiten. Das ist kein Bereich, den man eigenmächtig anfassen sollte. ...und genau das Gleiche gilt für für jeden anderen Namespace einer externen Bibliothek.
volkard schrieb:
Wo kommt man denn da hin, wenn sich jeder seinen eigenen Namespace std "definiert". Ein Wartungsalptraum. Wenn ein anderer Deinen Code liest, wird er ganz schön lange nach der Definition dieser Funktionen suchen.
ja. rechtsklick auf den funktionsnamen und aussuchen des kontextmenupunks "go to definition". ewig lange zwei sekunden später hat er sie. welche geschwindigkeiten bist du mit eclypse so gewohnt, daß du 2 sekunden (inklusive zweimal klicken) für ganz schön lang halten tust?
Oh... ich habe noch nie die Zeit gemessen... aber wenn da irgendwo Math.sqrt in einem Javacode steht, dann sehe ich unabhängig von der genutzten IDE oder des genutzten Editors sofort, dass das sqrt aus der Standardbibliothek kommt. Setzen wir also mal 0,1 Sekunden an. ...Faktor 20 schneller und man ist noch nichtmal auf die IDE angewiesen.
Viel wichtiger halte ich allerdings, dass ich in keinster Weise von meinem Lesefluss gestört werde: Ich muss nicht zur Maus greifen, um zu erkennen, was dort Sache ist.
-
Ist Objektorientierung nicht genau der engegengesetzer Weg ?
Man definiert ein Interface, aber man erweitert das Interface doch nicht. Wenn du eine neue Funktion im Namespace std für deinen MyInteger definierst, dann erweiterst du das Interface von sqrt.
Der richtige Weg wäre einen Operator int() in deiner MyInteger-Klasse zu definieren ( oder eben float/double ). So das sqrt() diesen Operator aufrufen kann.
Wenn ich einen std:sqrt(myinteger) sehe, dann schau ich erstmal stundenlang in die STL-Doku wo denn so ein sqrt definiert ist.
Du erweiterts doch die Funktion std:sort() doch nicht um deine ShadeListe. Sondern du definierst in der Klasse ShadeListe das Interface iterator.
-
Gregor schrieb:
konstruieren) ...ok, bei EIGENEN Typen, die irgendwo in der Signatur auftauchen, ist das natürlich äußerst unwahrscheinlich. Aber es geht ja generell um die Erweiterungsfähigkeit des Namespaces.
nö. es geht ab jetzt um die erweiterung des std-namespaces für eigene typen.
Ich bin halt der Meinung, dass std vom Standard her vorgegeben ist.
(hier frage ich vorhin "warum" und du hast leer zurückgeantwortet. das vereinfache ich jetzt mal.)
quatsch.Viel wichtiger halte ich allerdings, dass ich in keinster Weise von meinem Lesefluss gestört werde: Ich muss nicht zur Maus greifen, um zu erkennen, was dort Sache ist.
ok, du weißt sofort, in welcher datei das steht. aber das istr fürchterlich egal, wo das steht. kannst immer hingehen.
mir ist wichtig, daß ich die bedeutung von std::sort kenne und wer std::sort überlädt, wird sich dran halten.
oder für ein einfacheres beispiel, nehmen wir mal std::swap.
natürlich biete ich std::swap auch für eigene typen an.
-
ok, du weißt sofort, in welcher datei das steht. aber das istr fürchterlich egal, wo das steht. kannst immer hingehen.
mir ist wichtig, daß ich die bedeutung von std::sort kenne und wer std::sort überlädt, wird sich dran halten.
oder für ein einfacheres beispiel, nehmen wir mal std::swap.
natürlich biete ich std::swap auch für eigene typen an.Ist das jetzt ein Scherz ? Für jeden Type den du hast erweiterst du die STL ?
Was ist wenn jetzt 2 (oder besser 10) Leute an einem Projekt arbeiten. Diese 2 erweitern also für jeden Type den sie haben die STL. Das Ergebniss kannste dir doch Vorstellen oder ?
Am Ende findet dann ein Kampf in einer Arena statt, wenn 2 Programmierer die STL für den gleichen Type erweitern. Naja das sorgt wenigestns für Aufregung und für sportliche Bewegung am Arbeitsplatzt.
-
DEvent schrieb:
Man definiert ein Interface, aber man erweitert das Interface doch nicht. Wenn du eine neue Funktion im Namespace std für deinen MyInteger definierst, dann erweiterst du das Interface von sqrt.
nein.
ein interface ist eine beschreibung der schnittstellen. Wenn wir jetzt sagen ein Objekt implementiert IMath wenn es sqrt() anbietet.
class MyInt : public IMath { public: virtual MyInt sqrt(); };
das wuerdest du vermutlich als OK einstufen, oder?
aber was ist
a.sqrt();
denn anderes als
sqrt(a);
?es ist das selbe, nur andere schreibweise.
In LISP macht man es zB immer so: man definiert eine funktion sqrt() und implementiert die dann fuer alle "Klassen" die es anbieten.
Der richtige Weg wäre einen Operator int() in deiner MyInteger-Klasse zu definieren ( oder eben float/double ). So das sqrt() diesen Operator aufrufen kann.
ne, bloss nicht.
Wenn ich einen std:sqrt(myinteger) sehe, dann schau ich erstmal stundenlang in die STL-Doku wo denn so ein sqrt definiert ist.
wieso?
sqrt ist doch perfekt dokumentiert: du schaust in die doku und da steht
int sqrt(int); //ermittelt die quadratwurzel einer zahl und returned dieseund dann muss ich nicht nachsehen ob das fuer double anders ist. weil es fuer double definitionsgemaess genau gleich ist.
oder vielleicht steht da sogar nur
template<typename T>
T sqrt(T);da das waere ja nice, oder? reine interface definiton
Du erweiterts doch die Funktion std:sort() doch nicht um deine ShadeListe. Sondern du definierst in der Klasse ShadeListe das Interface iterator.
und da ein sort leider ewig dauern wuerde, ich aber mit ein paar tricks das sortieren enorm schnell hinbekomme (weil ich ja die implementationsdetails meiner ShadeList kenne) baue ich mir eine spezialisierung fuer std::sort.
klar, es ist etwas "out of the box" denken noetig. aber OOP hat echt nichts mit Klassen zu tun. vergiss klassen. funktionen koennen genauso ein interface definieren.
-
DEvent schrieb:
Ist das jetzt ein Scherz ? Für jeden Type den du hast erweiterst du die STL ?
nein. wir spezialisieren.
wenn ich eine Klasse Bitmap habe die intern halt nen char* auf die daten haelt, dann kann ich natuerlich bei einem std::sort() auf ein array von bitmaps jedesmal n copy machen, oder aber std::swap spezialisieren.
dann spezialisiere ich std::swap so, dass er nur schnell die 2 pointer tauscht.
Ich habe die STL nicht erweitert, es ist keine funktionalitaet dazu gekommen.
ich haette genauso
class Bitmap : public ISwapable { public: virtual void swap(Bitmap&); };
machen koennen. nur dass es mit statischer polymorphie und std::swap einfach soviel besser ist.
Was ist wenn jetzt 2 (oder besser 10) Leute an einem Projekt arbeiten. Diese 2 erweitern also für jeden Type den sie haben die STL. Das Ergebniss kannste dir doch Vorstellen oder ?
ja. wunderbarer code.
Am Ende findet dann ein Kampf in einer Arena statt, wenn 2 Programmierer die STL für den gleichen Type erweitern. Naja das sorgt wenigestns für Aufregung und für sportliche Bewegung am Arbeitsplatzt.
wieso sollten sie das tun?
dann kannst du genauso sagen es macht keinen sinn klassen zu verwenden, weil ja jeder eine funktion draw() einbauen koennte und dann hast du 10 draw() funktionen...ne, ne, ne. soviel uebersicht muss das team schon haben dass sie jede schnittstelle einer klasse nur einmal implementieren.
std::swap ist nichts anderes als eine implementierung des interfaces ISwapable. die std::swap spezialisierung findet sich auch direkt in der datei mit der interface definition der klasse. also da kann man nix doppelt implementieren...
PS:
es wir keine neue funktion hinzugefuegt. der std namespace bekommt nicht mehr funktionalitaet. es kommt nichts neues dazu.ich implementiere lediglich ein interface. mehr nicht.
nur dass das interface halt statisch ist und nicht dynamisch. (statische polymorphie ist auch polymorphie ;))
-
A* a = new A;
ist auch nicht OO!
hier wird der KLASSE A die Nachricht "new" geschickt, ein Objekt A zu erstellen. Im OO-System schickt man jedoch Nachrichten an OBJEKTE. Allgemein passen statische Funktionen nicht in das OO-Paradigma... was nun?zu namespaces vs. static imports:
Klar, man hätte in java vielleicht Namespaces einführen können, da Math nicht wirklich eine schöne Klasse darstellt. Aber was ich in c++ doof finde, ist, dass ich bei Namespaces "::" und bei Objekten oder Klassen "." schreiben muss. In Java muss ich in der IDE "." schreiben, und schon listet mir die Code-Completion alles auf. In c++ muss ich vorher genau wissen, ob Math nun eine Klasse oder ein Namespace ist....
-
OOler schrieb:
In c++ muss ich vorher genau wissen, ob Math nun eine Klasse oder ein Namespace ist....
du musst wissen ob Math ein objekt ist oder ein namespace/klasse.
bedeutender unterschied.
aber wir wollen ja nicht die syntax diskutieren oder? denn die ist grausam, wie man es auch dreht und wendet.
-
Shade Of Mine schrieb:
OOler schrieb:
In c++ muss ich vorher genau wissen, ob Math nun eine Klasse oder ein Namespace ist....
du musst wissen ob Math ein objekt ist oder ein namespace/klasse.
bedeutender unterschied.
stimmt :), so bedeutend ist aber dieser unterschied für diesen fall nicht. bei java ist es beim "." egal, ob du nun ein packet, klasse oder objekt "ansprichst". damit abstrahiert bereits die syntax die drei konzepte zur dekomposition deines systems.
-
OOler schrieb:
damit abstrahiert bereits die syntax die drei konzepte zur dekomposition deines systems.
ob das im entferntesten sinnvoll sein kann, sei dahingestellt.
es wäre kein problem, das einer c++-ide auch beizubringen, daß man nur . oder :: tippt und auf beide gleich mit der komplettliste reagiert wird.
-
sqrt ist doch perfekt dokumentiert: du schaust in die doku und da steht
int sqrt(int); //ermittelt die quadratwurzel einer zahl und returned dieseund dann muss ich nicht nachsehen ob das fuer double anders ist. weil es fuer double definitionsgemaess genau gleich ist.
Manche Funktionen sind Typenabhänig.
Z.B. Die Division. int div(int x, int y) ist was anderes als double div(double x, double y). Auch die Funktion betrag() ist was anderes für normale Zahlen, Vektoren, komplexe Zahlen.
Muss gestehen das ich mir bei diesem Argument nicht ganz sicher bin. Ist bestimmt ziemlich schwaches Argument.Mal das Beispiel div:
int div(int x, int y).
Man kann es verschieden Implementieren, man erhält verschiedene Ergebnisse.int div(int x, int y) { double y = (double)x / (double)y; return round_abwärst(y); }
int div(int x, int y) { double y = (double)x / (double)y; return round_aufwärst(y); }
int div(int x, int y) { double y = (double)x / (double)y; return schneide_nachkomme_ab(y); }
int div(int x, int y) { return y = ganzzahlige_division(x, y); }
naja usw usf.
class Bitmap : public ISwapable { public: virtual void swap(Bitmap&); };
Das Beispiel auf Namespaces bezogen würde doch eher so aussehen:
namespace bitmap { void swap(Bitmap& b) { std:swap(b.intern_daten); } }
Wieso sollte deine spezielle Funktion eigentlich überhaupt im Namespace std liegen? Gibt es dafür einen Grund (ausser den Leser des Codes zu verwirren) ?
In der Bibliothek STL wird doch nirgendwo deine spezielle Funktion aufgerufen.
dann kannst du genauso sagen es macht keinen sinn klassen zu verwenden, weil ja jeder eine funktion draw() einbauen koennte und dann hast du 10 draw() funktionen...
Klar gibt es 10 draw()-Methoden, aber sie liegen in 10 verschiedenen Klassen. Du hast 10 draw()-Funktionen und sie liegen alle im namespace std.
ne, ne, ne. soviel uebersicht muss das team schon haben dass sie jede schnittstelle einer klasse nur einmal implementieren.
Eben nicht, die Sprache sollte dies "von Haus aus" absichern. Das war auch der Gedanke bei Namespace in C++, packages bei Java usw.
Mir fällt auch grade ein, das du das Bitmap-Beispiel falsch hast.
Das Interface Swapable kann nicht so sein, es müsste:public interface Swapable { void swap(Object a); } public class Bitmap implements Swapable { void swap(Object a) { Bitmap b = (Bitmap)a; b.spaw_data(); } }
Bei deiner "statischen Polymorphie" erweiterst du das Interface Swapable um eine Methode: void swap(Bitmap b);
oder vielleicht steht da sogar nur
template<typename T>
T sqrt(T);Das wäre das optimale. Jetzt kann sqrt() mit allem umgehen, das den Operator double() anbietet. Was spricht den dagegen, das du so dagegen bist?
-
DEvent schrieb:
Manche Funktionen sind Typenabhänig.
Z.B. Die Division. int div(int x, int y) ist was anderes als double div(double x, double y). Auch die Funktion betrag() ist was anderes für normale Zahlen, Vektoren, komplexe Zahlen.nicht wirklich. ein div sollte fuer int und double wohl gleich aussehen...
Das Beispiel auf Namespaces bezogen würde doch eher so aussehen:
namespace bitmap { void swap(Bitmap& b) { std:swap(b.intern_daten); } }
und wieso ist das besser als swap() in std zu packen?
ich kann dann halt nicht mehr:
template<typename T> void foo(T& a, T& b) { std::swap(a,b); }
machen. ich darf das std nicht dazu schreiben und der koenig lookup kommt dann rein. naja... ist das wirklich schoener?
Wieso sollte deine spezielle Funktion eigentlich überhaupt im Namespace std liegen? Gibt es dafür einen Grund (ausser den Leser des Codes zu verwirren) ?
was verwirrt dich?
In der Bibliothek STL wird doch nirgendwo deine spezielle Funktion aufgerufen.
und?
Klar gibt es 10 draw()-Methoden, aber sie liegen in 10 verschiedenen Klassen. Du hast 10 draw()-Funktionen und sie liegen alle im namespace std.
wo ist das problem wenn sqrt fuer 10 verschiedene typen implementiert ist? was stoert daran?
Eben nicht, die Sprache sollte dies "von Haus aus" absichern. Das war auch der Gedanke bei Namespace in C++, packages bei Java usw.
du verstehst nicht.
ich kann nur ein MyInt sqrt(MyInt) anlegen genau wie ich nur ein MyInt::sqrt() anlegen kann. der sprache sichert das ab.
Mir fällt auch grade ein, das du das Bitmap-Beispiel falsch hast.
Das Interface Swapable kann nicht so sein, es müsste:public interface Swapable { void swap(Object a); } public class Bitmap implements Swapable { void swap(Object a) { Bitmap b = (Bitmap)a; b.spaw_data(); } }
Bei deiner "statischen Polymorphie" erweiterst du das Interface Swapable um eine Methode: void swap(Bitmap b);
schau es dir nochmal an und dann sag mir dass statische polymoprhie hier nicht besser ist.
du verstehst etwas nicht:
ich fuege keine funktion hinzu. es kommt keine funktionalitaet hinzu. auch wenn swap physikalisch in std liegt ist es teil des interfaces von Bitmap.
-
Shade Of Mine schrieb:
auch wenn swap physikalisch in std liegt ist es teil des interfaces von Bitmap.
Der Satz muss einem doch schon zu denken geben. "a gehört zu b, ist aber an ganz anderer Stelle zu finden". Mir sagt da mein Instinkt, dass das nicht gut sein kann.
BTW: Definiere mal "Interface".
-
Gregor schrieb:
BTW: Definiere mal "Interface".
Math bietet ein interface an. Es definiert funktionen die auf ein objekt angewendet werden koennen.
ihr seht das alles viel zu konservativ. dass man std::swap spezialisiert ist im c++ standard so vorgesehen, dass ist keine kranke erfindung von mir oder volkard.
es ist eben ein anderes denken. wie gesagt: in lisp definiert man _alle_ methoden auf exakt diese art.
OOP hat nix mit klassen zu tun. klingt komisch, ist aber so.
ich mach ein Math.sqrt(a); und egal was a ist, sofern es das Math.sqrt interface implementiert liefert es mir die quadratwurzel.
es ist wirklich nur eine andere schreibweise fuer herkoemmliche interfaces. wir koennen uns streiten in welchen namespace das gehoert, aber es spricht nichts gegen den namespace std. denn es kann zu keiner kollision kommen und die funktion sqrt liegt in der selben datei definiert wie die klasse.
ich weiss, es ist komisch. aber so funktioniert statische polymorphie.
-
Shade Of Mine schrieb:
OOP hat nix mit klassen zu tun. klingt komisch, ist aber so.
Wir reden hier ja wohl nicht über OOP, oder? Namespaces haben genausowenig mit OOP zu tun, wie Klassen, in denen lauter statische Methoden anzutreffen sind. In der OOP ist "Schnittstelle" ein genau definierter Begriff, der nicht auf ein swap in irgendeinem Namespace angewandt werden kann. Deshalb frage ich nach der Definition. Auf welchen Schnittstellenbegriff beziehst Du Dich?
-
Gregor schrieb:
Auf welchen Schnittstellenbegriff beziehst Du Dich?
vergiss die namespaces. sie verwirren dich und lenken vom thema ab.
wie wuerdest du folgendes nennen?
du hast 3 Typen: Integer, int, double.
Alle 3 Typen bieten dir folgende operation an:
sqrt()du kannst
a.sqrt();
machen unabhaengig ob a jetzt den Typ Integer, int oder double hat.Wuerdest du jetzt sagen dass Integer, int und double ein interface anbieten dass sqrt implementiert?
Im prinzip schon, oder?
nun stellen wir uns vor das ganze ist nun anders rum:
Integer, int und double sind keine klassen die ein interface implementieren. sondern sie implementieren eine funktion.das selbe prinzip, nur anders rum
exakt das haben wir hier: es sind objekte. es ist polymoprhie. Es ist aber auch statisch. und sqrt gehoert nun eben zu Math. Und ich implementiere das interface indem ich sqrt spezialisiere fuer meinen typen...
-
ich kann nur ein MyInt sqrt(MyInt) anlegen genau wie ich nur ein MyInt::sqrt() anlegen kann. der sprache sichert das ab.
Nein die Sprache sichert es eben nicht ab.
Wenn Entwickler A an einem Modul Bildschirm arbeitet und Entwickler B an Modul Drucker. Beide Entwickler brauchen die Funktion sqrt(double). Entwickler A implementiert nun sqrt(double) steckt es in namespace std. Entwickler B macht genau das selbt.
Am Ende, wenn beide Module in einen Programm verwendet werden gibt es haufenweise Compilerfehler, weil es zig sqrt(double) im namespace std gibt.Wozu war das nun gut?
Anstatt das Entwickler A sein sqrt() in namespace bildschirm und Entwickler B sein sqrt() in namespace drucker reinschreibt, damit es keine Mehrdeutigkeiten gibt.
Deine Methode bietet einfach keineler Vorteile, mehr noch sie verursacht haufen Probleme.
Das selbe ist halt mit Klassen.
Zur besseren Verwaltung gleichartiger Objekte bedienen sich die meisten Programmiersprachen des Konzeptes der Klasse. Klassen sind Vorlagen, aus denen Objekte zur Laufzeit erzeugt werden (Instanzen). Im Programm werden dann nicht einzelne Objekte, sondern eine Klasse gleichartiger Objekte definiert.
Man kann sich die Erzeugung von Objekten aus einer Klasse vorstellen wie das Fertigen von Autos aus dem Konstruktionsplan eines bestimmten Fahrzeugtyps. Klassen sind die Konstruktionspläne für Objekte.
Also du willst neue Eigenschaften hinzufügen. Soetwas macht man aber durch Vererbung. Vererbung heißt aber das die Basis-Klasse inverändert bleibt. Sie wird spezialisiert durch Unterklassen.
Du aber veränderst die Basisklasse ( namespace std ) und speziallisierst sie so. Das ist kein OOP.exakt das haben wir hier: es sind objekte. es ist polymoprhie. Es ist aber auch statisch. und sqrt gehoert nun eben zu Math. Und ich implementiere das interface indem ich sqrt spezialisiere fuer meinen typen...
Deswegen gehört dein sqrt nicht mehr in Math. Dein sqrt gehört in deine Klasse / namespace what ever.
-
Gregor schrieb:
Shade Of Mine schrieb:
OOP hat nix mit klassen zu tun. klingt komisch, ist aber so.
Wir reden hier ja wohl nicht über OOP, oder? Namespaces haben genausowenig mit OOP zu tun, wie Klassen, in denen lauter statische Methoden anzutreffen sind. In der OOP ist "Schnittstelle" ein genau definierter Begriff,
der wesentlich breiter gefaßt ist, als das interface-schlüsselwort.
der nicht auf ein swap in irgendeinem Namespace angewandt werden kann.
doch.