Wie erstelle ich ein Interface korrekt?
-
Hallo zusammen.
Ich möchte mir ein Interface für eine Datenbankverbindung erstellen. Dazu wollte ich follgende abstrakte Methoden deklarieren:
virtual void connect() = 0; virtual bool connected() = 0; virtual void disconnect() = 0;Ich geh mal davon aus, das es ganz sinnvoll ist, diese Methoden abstrakt zu deklarieren. Leider bin ich was unsicher, was folgende Methoden angeht:
virtual void setHost(const std::string &hostName) = 0; virtual void setUser(const std::string &user) = 0; virtual void setPwd(const std::string &pwd) = 0; virtual const std::string & host() const; virtual const std::string & user() const; virtual const std::string & pwd() const;Diese Methoden werden unbestritten benötigt, um die Verbindungsparameter zu setzen(zumindest die Setter
)
Allerdings bieten solche Methoden nur Zugriff auf Attribute einer Klasse. Aber mein Interface besitzt ja keine Attribute.
Diese müßten die abgeleiteten Klassen enthalten. Daher meine Frage:Sollten in Interfaces nur "Worker" abstrakt deklariert werden,
virtual void connect() = 0;oder definiert ihr auch Methoden, die den Zugriff auf Attribute gestattet?
virtual void setHost(const std::string &hostName) = 0; virtual const std::string & host() const;Egal wie ich das Interface implementiere, es wird sehr warscheinlich funktionieren. Allerdings wollte ich nicht unbedingt den dümmsten Weg einschlagen

Ich fänd's schön, wenn ihr mir mit euerer Erfahrung helfen könntet.
NuckNuck
[Edit]
Tippfehler bei connected()
-
Hallo
Wenn du ein reines abstaktes Interface willst, must du auch die Implementierung von Gettern und Settern in die abzuleitenden Klassen verlegen.
Wenn du "nur" eine Basisklasse willst, die der kleinste gemeinsame Nenner aller möglichen Datenbankverbindungen sein soll (ausgehend vom üblichen Standard mit Host, Port, Login, Password...), dann kannst du solche allgemeinen Attribute auch in die Basisklasse implementieren.bis bald
akari
-
Du definnierst eine allgemeine Schnittstelle zum Zugriff auf Datenbanken. Datenbanken laufen im allgemeinen auf einem Host und man muß sich an ihnen per Login und Passwort anmelden...
Daher würde ich diese Zugriffsmethoden als allgemein ansehen auch wenn deren Implementierung nicht durch die Schnittstelle erfolgt.
Allerdings solltest du berücksichtigen, dass hinter Host/Benutzer/Passwort auch ein nicht veränderbarer Wert sein kann, also nicht zwingend setzbar sein muß.
Wenn du die Funktionen nur in den abgleiteten Klassen deklarierst/definierst wird die Schnittstelle nur eingeschränkt nutzbar, da du doch wieder zwischen den unterschiedlichen abgeleiteten Klassen unterscheiden mußt.
-
Danke akari, für die verdammt schnelle Antwort.
Jetzt kommt die Kretchenfrage

Was wäre deiner Meinung denn sinnvoller?
Ein Interface, in dem die Getter und Setter abstrakt sind, oder "nur" eine Basisklasse?Ups!
Ich stelle fest, das ich nicht genau weiß, wann ein Interface und wann eine Basisklasse zu verwenden ist.
Vielleicht kann mir da jemand auf die Sprünge helfen...
-
@Mathias
Ich hoffe ich hab dich richtig verstanden.
Sobald eine Methode so allgemein ist, das sie für alle abgeleiteten Klassen gültig ist, sollte sie in das Interface / die Basisklasse.Ich hatte noch im Kopf, das Getter und Setter normalerweise gute Kandidaten für inline Methoden sind. Das wird jedoch was schwierig, wenn diese inline sind.
Oder täusche ich mich da?
NuckNuck
-
nucknuck schrieb:
Ein Interface, in dem die Getter und Setter abstrakt sind, oder "nur" eine Basisklasse?
Das hängt von deiner Datenverwaltung ab - wenn jede (echte) DB-Klasse ihren eigenen Weg findet, Host etc zu speichern, reichen in der Basisklasse die abstrakten Methoden. Wenn alle abgeleiteten Klassen die gleiche Implementierung nutzen, kannst du die Getter/Setter inklusive der dahinterliegenden Datenelemente auch in der Basisklasse implementieren (auch die abgeleiteten Klassen haben das Recht, auf diese Methoden zuzugreifen, um z.B. ihren Connection String zusammenzubauen).
Ich stelle fest, das ich nicht genau weiß, wann ein Interface und wann eine Basisklasse zu verwenden ist.

In C++ gibt es keine Interfaces. Da gibt es nur abstakte und konkrete Klassen (erstere können NUR als Basisklasse verwendet werden - und sind wohl die beste Näherung an Interfaces, die C++ zu bieten hat).
-
@CStoll
Wenn ich also eine allgemeine Methode implementiere, um auf eine Datenbank zuzugreifen, dann implementiere ich eine Basisklasse.
Möchte ich nur die Schnittstelle vorgeben und die Implementierung den abgeleiteten Klassen überlassen, dann ist ein Interface angebracht.Ich hoffe ich das jetzt richtig zusammengefasst.
@Alle
Danke für die fixen und guten Antworten
NuckNuck
-
Ich würde die Getter/Setter nicht mit in das Interface stecken. Derjenige, der das DB-Connection-Objekt instanziert muß eh eine konkrete Klasse haben, die dann diese Setter anbietet. Für den späteren Programmverlauf sind diese Setter und Getter unwichtig. Sollten diese gebraucht werden, sollte z.B. der Client wissen, welche konkrete Klasse er mal instanziert hat.
Alternativ könnte man auch ein Context-Shield-Pattern einsetzen. D.h. du schreibst zwei Interfaces: eine große Klasse und ein abgespecktes Interface, welches nur die nötigsten hält.
Beispiel (Pseudocode!):
class BigConnection { public: name; // setter/getter password; // setter/getter host; // setter/getter };So, in die BigConnection (Name neigt zur Übertreibung) steht alles wichtige und für komfort nötige.
Dann kannst du eine Context-Klasse/Interface bauen:class ConnectionContext { private: BigConnection*; // die wird nicht bekannt gegeben!!! auch nicht über getter! public: name; // nur getter password; // nur getter host; // nur getter }Bau dir dann in die BigConnecetion so ne Art Faktory-Methode rein, die alles instanziert und dem User aber nur eine ConnectioContext zurück gibt. Das hat auch den Vorteil, das in der BigConnection kritische public-Methode stehen können, die aber der User des Context niemals erreichen wird. Aber über den Context gibst du ihm das raus, was du ihm erlauben willst.
So hast du trotzdem eine kleine Schnittstelle für den User.
Ich finde das Context Shield Pattern einfach nur genial.

-
Du würfelst da was durcheinander...
Zuerst kommt das Design. Dort entscheidet man wo was sichtbar ist (und da kann es sein, dass ich in einer Schnittstelle auch den Zugriff auf einen Datensatz erlaube).
Ob eine Funktion inline ist oder nicht entscheidet man erst später beim Implementieren.
-
Wenn eine Klasse per Definition einen Attributsatz besitzt, sehe ich keinen Grund diesen nicht auch zugreifbar zu machen unabhängig davon ob jetzt die Klasse den Attributsatz implementiert oder erst die abgeleiteten Klassen.
(Was natürlich nicht heißt, das man es auch anders implementieren kann. Viele Wege führen nach Rom...)

-
@Artchi
Hmm, dieses Context Shield Pattern war mir bisher unbekannt. Das werd ich mir mal genauer ansehen. Danke für den Tipp.Meine Überlegung für ein Interface war folgende:
- Der Benutzer der Verbindung soll nur die Verbindungdaten(host, user, ...) besitzen
- Die Art der Verbindung soll für den Benutzer keine Rolle spielen (MySQL, Oracle, ...)
- Das Verbindungsobjektes soll über eine Factory erzeugt werdenDas ganze sollte ungefähr so zu verwenden sein:
BasisConnection *con = Factory::createConnection(host); con->setUser(""); con->setPwd(""); con->setPort(12345); con->connect();Nun gut, die Klasse besitzt per Definition die obigen Attribute. Dann sollte es ja auch ok sein, wenn die Getter und Setter abstrakt deklariert werden.
Ich wollte das ganze einfach nicht von vorneherein falsch angehen.
Daher Danke für die vielen Antworten.NuckNuck