Welche Rolle spielen Singletons, abstrakte Klassen und Interfaces?


13

Ich lerne OOP in C ++ und obwohl mir die Definitionen dieser 3 Konzepte bekannt sind, kann ich nicht wirklich erkennen, wann oder wie ich es verwenden soll.

Verwenden wir diese Klasse für das Beispiel:

class Person{
    private:
             string name;
             int age;
    public:
             Person(string p1, int p2){this->name=p1; this->age=p2;}
             ~Person(){}

             void set_name (string parameter){this->name=parameter;}                 
             void set_age (int parameter){this->age=parameter;}

             string get_name (){return this->name;}
             int get_age (){return this->age;}

             };

1. Singleton

WIE funktioniert die Einschränkung der Klasse, nur ein Objekt zu haben?

KÖNNEN Sie eine Klasse entwerfen, die NUR 2 Instanzen haben würde? Oder vielleicht 3?

WANN wird ein Singleton empfohlen / benötigt? Ist es eine gute Übung?

2. Abstrakte Klasse

Soweit ich weiß, wird die Klasse abstrakt, wenn es nur eine rein virtuelle Funktion gibt. Also, Hinzufügen

virtual void print ()=0;

würde es tun, richtig?

WARUM brauchen Sie eine Klasse, deren Objekt nicht benötigt wird?

3.Interface

Wenn eine Schnittstelle eine abstrakte Klasse ist, in der alle Methoden reine virtuelle Funktionen sind, dann

WAS ist der Hauptunterschied zwischen den beiden?

Danke im Voraus!


2
Singleton ist umstritten. Suchen Sie auf dieser Site danach, um verschiedene Meinungen zu erhalten.
Winston Ewert

2
Es ist auch erwähnenswert, dass abstrakte Klassen Teil der Sprache sind, Singletons und Interfaces jedoch nicht. Sie sind Muster, die Menschen implementieren. Insbesondere Singleton ist etwas, das ein bisschen cleveres Hacken erfordert, damit es funktioniert. (Natürlich können Sie auch nur nach Vereinbarung einen Singleton erstellen.)
Gort the Robot

1
Einer nach dem anderen bitte.
JeffO

Antworten:


17

1. Singleton

Sie schränken die Anzahl der Instanzen ein, da der Konstruktor privat ist, was bedeutet, dass nur statische Methoden Instanzen dieser Klasse erstellen können (es gibt andere schmutzige Tricks, um dies zu erreichen, aber wir lassen uns nicht mitreißen).

Das Erstellen einer Klasse mit nur 2 oder 3 Instanzen ist problemlos möglich. Sie sollten Singleton immer dann verwenden, wenn Sie der Meinung sind, dass nur eine Instanz dieser Klasse im gesamten System vorhanden sein muss. Das passiert normalerweise bei Klassen, die ein "Manager" -Verhalten haben.

Wenn Sie mehr über Singletons erfahren möchten, können Sie in diesem Beitrag in Wikipedia und speziell für C ++ beginnen .

Es gibt definitiv einige gute und schlechte Dinge an diesem Muster, aber diese Diskussion gehört woanders hin.

2. Abstrakte Klassen

Ja, das ist richtig. Nur eine einzelne virtuelle Methode markiert die Klasse als abstrakt.

Sie werden solche Klassen verwenden, wenn Sie eine größere Klassenhierarchie haben, in der die Hauptklassen nicht wirklich instanziiert werden sollten.

Nehmen wir an, Sie definieren eine Säugetierklasse und erben sie dann an Hund und Katze. Wenn Sie darüber nachdenken, hat es keinen Sinn, ein reines Exemplar eines Säugetiers zu haben, da Sie zuerst wissen müssen, was für ein Säugetier es wirklich ist.

Möglicherweise gibt es eine Methode namens MakeSound (), die nur in den geerbten Klassen Sinn macht, aber es gibt keinen gemeinsamen Klang, den alle Säugetiere erzeugen können (dies ist nur ein Beispiel, bei dem nicht versucht wird, die Geräusche von Säugetieren zu berücksichtigen).

Das bedeutet, dass Säugetiere eine abstrakte Klasse sein sollten, da sie ein für alle Säugetiere gemeinsames Verhalten aufweisen, aber nicht wirklich instanziiert werden sollten. Das ist das Grundkonzept hinter abstrakten Klassen, aber es gibt definitiv mehr, was Sie lernen sollten.

3. Schnittstellen

In C ++ gibt es keine reinen Schnittstellen in demselben Sinne wie in Java oder C #. Die einzige Möglichkeit, eine zu erstellen, besteht darin, eine reine abstrakte Klasse zu haben, die den größten Teil des Verhaltens nachahmt, das Sie von einer Schnittstelle erwarten.

Grundsätzlich besteht das gesuchte Verhalten darin, einen Vertrag zu definieren, mit dem andere Objekte interagieren können, ohne sich um die zugrunde liegende Implementierung zu kümmern. Wenn Sie eine Klasse rein abstrakt machen, bedeutet dies, dass die gesamte Implementierung an einen anderen Ort gehört. Der Zweck dieser Klasse besteht also nur in dem Vertrag, den sie definiert. Dies ist ein sehr leistungsfähiges Konzept in OO und Sie sollten es auf jeden Fall genauer untersuchen.

Weitere Informationen zur Schnittstellenspezifikation für C # in MSDN finden Sie unter:

http://msdn.microsoft.com/en-us/library/ms173156.aspx

C ++ bietet die gleiche Art von Verhalten, indem es eine reine abstrakte Klasse hat.


2
Eine reine abstrakte Basisklasse bietet Ihnen alles, was eine Schnittstelle tut. In Java (und C #) gibt es Schnittstellen, da die Sprachentwickler die Mehrfachvererbung verhindern wollten (wegen der damit verbundenen Kopfschmerzen), aber eine sehr häufige Verwendung der Mehrfachvererbung erkannt haben, die nicht problematisch ist.
Gort the Robot

@StevenBurnap: Aber nicht in C ++, das ist der Kontext der Frage.
DeadMG

3
Er fragt nach C ++ und Schnittstellen. "Interface" ist kein Sprachfeature von C ++, aber die Leute erstellen in C ++ Interfaces, die genau wie Java-Interfaces funktionieren, mit abstrakten Basisklassen. Sie taten dies, bevor es Java überhaupt gab.
Gort the Robot


1
Gleiches gilt für Singletons. In C ++ sind beide Entwurfsmuster und keine Sprachfunktionen. Das bedeutet nicht, dass die Leute nicht über Schnittstellen in C ++ sprechen und wofür sie gedacht sind. Das Konzept der "Schnittstelle" entstand aus Komponentensystemen wie Corba und COM, die beide ursprünglich für die Verwendung in reinem C entwickelt wurden. In C ++ werden Schnittstellen normalerweise mit abstrakten Basisklassen implementiert, in denen alle Methoden virtuell sind. Die Funktionalität ist identisch mit der einer Java-Schnittstelle. Als solches ist das Konzept einer Java-Schnittstelle absichtlich eine Teilmenge von abstrakten C ++ - Klassen.
Gort the Robot

8

Die meisten Leute haben bereits erklärt, was Singletons / abstrakte Klassen sind. Hoffentlich gebe ich eine etwas andere Perspektive und gebe einige praktische Beispiele.

Singletons - Wenn Sie möchten, dass der gesamte aufrufende Code aus beliebigen Gründen eine einzige Instanz von Variablen verwendet, haben Sie die folgenden Optionen:

  • Globale Variablen - offensichtlich keine Kapselung, der Großteil des Codes ist an globale Variablen gekoppelt ... schlecht
  • Eine Klasse mit allen statischen Funktionen - ein bisschen besser als einfache globale Funktionen, aber diese Entwurfsentscheidung führt Sie immer noch zu einem Pfad, in dem Code auf globalen Daten beruht und später möglicherweise nur sehr schwer zu ändern ist. Sie können auch keine Vorteile aus OO-Dingen wie Polymorphismus ziehen, wenn Sie nur statische Funktionen haben
  • Singleton - Obwohl es nur eine Instanz der Klasse gibt, muss die tatsächliche Implementierung der Klasse nichts über die Tatsache wissen, dass sie global ist. So können Sie heute eine Klasse haben, die ein Singleton ist, und morgen können Sie einfach den Konstruktor veröffentlichen und Clients mehrere Kopien instanziieren lassen. Der meiste Clientcode, der auf den Singleton verweist, muss nicht geändert werden, und die Implementierung des Singleton selbst muss nicht geändert werden. Die einzige Änderung besteht darin, wie der Client-Code zunächst eine Singleton-Referenz erhält.

Wenn Sie globale Daten benötigen, ist Singleton von allen schlechten und bösen Optionen ein VIEL besserer Ansatz als die beiden vorherigen. Sie können Ihre Optionen auch offen halten, wenn Sie morgen Ihre Meinung ändern und statt globaler Daten die Umkehrung der Kontrolle verwenden.

Wo würden Sie einen Singleton verwenden? Hier sind einige Beispiele:

  • Protokollierung - Wenn Sie möchten, dass Ihr gesamter Prozess ein einziges Protokoll enthält, können Sie ein Protokollobjekt erstellen und es überall weitergeben. Aber was ist, wenn Sie 100.000 Zeilen Legacy-Anwendungscode haben? alle modifizieren? Oder stellen Sie einfach Folgendes vor und beginnen Sie damit, wo immer Sie möchten:

    CLog::GetInstance().write( "my log message goes here" );
  • Serververbindungs-Cache - Dies musste ich in unserer Anwendung einführen. Unsere Codebasis, und es gab eine Menge davon, wurde verwendet, um eine Verbindung zu Servern herzustellen, wann immer es ihnen gefiel. Meistens war dies in Ordnung, es sei denn, im Netzwerk gab es Latenzzeiten. Wir brauchten eine Lösung und das Redesign einer 10 Jahre alten Anwendung stand nicht wirklich auf dem Tisch. Ich habe einen einzelnen CServerConnectionManager geschrieben. Dann durchsuchte ich den Code und ersetzte CoCreateInstanceWithAuth-Aufrufe durch einen identischen Signaturaufruf, der meine Klasse aufrief. Jetzt, nachdem der erste Verbindungsversuch zwischengespeichert wurde und der Rest der Zeit "Verbindungsversuche" sofort erfolgte. Einige sagen, Singletons sind böse. Ich sage, sie haben meinen Hintern gerettet.

  • Für das Debuggen erweisen sich globale laufende Objekttabellen häufig als sehr nützlich. Wir haben einige Kurse, die wir gerne nachverfolgen möchten. Sie stammen alle aus derselben Basisklasse. Während der Instanziierung rufen sie den Objekttabellen-Singleton auf und registrieren sich. Wenn sie zerstört werden, geben sie die Registrierung auf. Ich kann zu jeder Maschine gehen, mich an einen Prozess anhängen und eine Liste laufender Objekte erstellen. Seit über einem halben Jahrzehnt im Produkt und ich hatte nie das Gefühl, dass wir jemals zwei "globale" Objekttabellen benötigen würden.

  • Wir haben einige relativ komplexe String-Parser-Utility-Klassen, die auf regulären Ausdrücken basieren. Klassen für reguläre Ausdrücke müssen initialisiert werden, bevor Übereinstimmungen ausgeführt werden können. Die Initialisierung ist etwas aufwendig, da dann eine FSM auf der Basis eines Parsing-Strings generiert wird. Danach können jedoch 100 Threads sicher auf die Klasse für reguläre Ausdrücke zugreifen, da sich einmal erstellte FSMs niemals ändern. Diese Parser-Klassen verwenden intern Singletons, um sicherzustellen, dass diese Initialisierung nur einmal erfolgt. Dies verbesserte die Leistung erheblich und verursachte keine Probleme aufgrund von "bösen Singletons".

Wenn Sie dies alles gesagt haben, müssen Sie bedenken, wann und wo Sie Singletons verwenden. 9 von 10 Mal gibt es eine bessere Lösung und auf jeden Fall sollten Sie diese stattdessen verwenden. Es gibt jedoch Situationen, in denen Singleton die absolut richtige Wahl für das Design ist.

Nächstes Thema ... Schnittstellen und abstrakte Klassen. Wie andere bereits erwähnt haben, ist Interface eine abstrakte Klasse, aber es geht darüber hinaus, indem durchgesetzt wird, dass es absolut KEINE Implementierung hat. In einigen Sprachen ist das Schlüsselwort interface Teil der Sprache. In C ++ verwenden wir einfach abstrakte Klassen. Microsoft VC ++ hat einen Schritt unternommen, um dies intern zu definieren:

typedef struct interface;

... Sie können also weiterhin das Schlüsselwort interface verwenden (es wird sogar als "echtes" Schlüsselwort hervorgehoben), aber für den eigentlichen Compiler ist es nur eine Struktur.

Wo würdest du das verwenden? Kehren wir zu meinem Beispiel einer laufenden Objekttabelle zurück. Angenommen, die Basisklasse hat ...

virtueller void print () = 0;

Es gibt deine abstrakte Klasse. Klassen, die Laufzeitobjekttabellen verwenden, werden alle von derselben Basisklasse abgeleitet. Die Basisklasse enthält allgemeinen Code zum Registrieren / Aufheben der Registrierung. Aber es wird niemals von selbst instanziiert. Jetzt kann ich Klassen ableiten (z. B. Anforderungen, Listener, Clientverbindungsobjekte ...), von denen jede print () implementiert, sodass jedes Objekt beim Anhängen an den Prozess und bei der Frage, was ausgeführt wird, seinen eigenen Status meldet.

Beispiele für abstrakte Klassen / Interfaces gibt es unzählige, und Sie verwenden sie definitiv viel häufiger (oder sollten es auch tun), als Sie Singletons verwenden würden. Kurz gesagt, sie ermöglichen es Ihnen, Code zu schreiben, der mit Basistypen funktioniert und nicht an die tatsächliche Implementierung gebunden ist. Auf diese Weise können Sie die Implementierung später ändern, ohne zu viel Code ändern zu müssen.

Hier ist ein weiteres Beispiel. Nehmen wir an, ich habe eine Klasse, die einen Logger implementiert, CLog. Diese Klasse schreibt in die Datei auf der lokalen Festplatte. Ich beginne mit der Verwendung dieser Klasse in meinen alten 100.000 Codezeilen. Überall. Das Leben ist gut, bis jemand sagt, hey lasst uns in die Datenbank schreiben anstatt in eine Datei. Jetzt erstelle ich eine neue Klasse, nenne sie CDbLog und schreibe in die Datenbank. Können Sie sich vorstellen, wie mühsam es ist, 100.000 Zeilen durchzugehen und alles von CLog zu CDbLog zu ändern? Alternativ könnte ich haben:

interface ILogger {
    virtual void write( const char* format, ... ) = 0;
};

class CLog : public ILogger { ... };

class CDbLog : public ILogger { ... };

class CLogFactory {
    ILogger* GetLog();
};

Wenn der gesamte Code die ILogger-Schnittstelle verwenden würde, müsste ich nur die interne Implementierung von CLogFactory :: GetLog () ändern. Der Rest des Codes würde nur automatisch funktionieren, ohne dass ich einen Finger rühren müsste.

Für weitere Informationen zu Benutzeroberflächen und gutem OO-Design empfehle ich dringend die agilen Prinzipien, Muster und Vorgehensweisen von Onkel Bob in C # . Das Buch ist mit Beispielen gefüllt, die Abstraktionen verwenden und einfache Erklärungen für alles liefern.


4

WANN wird ein Singleton empfohlen / benötigt? Ist es eine gute Übung?

Noch nie. Schlimmer noch, sie sind eine absolute Hündin, die man loswerden muss. Wenn man diesen Fehler einmal macht, kann das viele, viele Jahre dauern.

Der Unterschied zwischen abstrakten Klassen und Interfaces ist in C ++ absolut nichts. Im Allgemeinen verfügen Sie über Schnittstellen, um ein bestimmtes Verhalten der abgeleiteten Klasse anzugeben, ohne jedoch alle angeben zu müssen. Dies macht Ihren Code flexibler, da Sie jede Klasse austauschen können, die der eingeschränkteren Spezifikation entspricht. Laufzeitschnittstellen werden verwendet, wenn Sie eine Laufzeitabstraktion benötigen.


Schnittstellen sind eine Teilmenge abstrakter Klassen. Eine Schnittstelle ist eine abstrakte Klasse ohne definierte Methoden. (Eine abstrakte Klasse ohne Code.)
Gort the Robot

1
@StevenBurnap: Vielleicht in einer anderen Sprache.
DeadMG

4
"Interface" ist nur eine Konvention in C ++. Wenn ich es gebraucht gesehen habe, ist es eine abstrakte Klasse mit nur rein virtuellen Methoden und keinen Eigenschaften. Natürlich können Sie jede alte Klasse schreiben und ein "I" vor den Namen setzen.
Gort the Robot

Dies ist die Art und Weise, wie ich erwartet hatte, dass die Leute diesen Beitrag beantworten. Eine Frage nach der anderen. Wie auch immer, vielen Dank, dass ihr euer Wissen geteilt habt. Diese Gemeinschaft ist eine Investition , beeilen Zeit in.
appoll

3

Singleton ist nützlich, wenn Sie nicht mehrere Kopien eines bestimmten Objekts möchten. Es darf nur eine Instanz dieser Klasse geben. Es wird für Objekte verwendet, die den globalen Status beibehalten und in irgendeiner Weise mit nicht wiedereintretendem Code umgehen müssen.

Ein Singleton mit einer festen Anzahl von 2 oder mehr Instanzen ist ein Multiton , denken Sie an Datenbankverbindungspooling usw.

Interface gibt eine gut definierte API an, mit deren Hilfe die Interaktion zwischen Objekten modelliert werden kann. In einigen Fällen kann es eine Gruppe von Klassen geben, die einige gemeinsame Funktionen haben. In diesem Fall können Sie der Schnittstelle Methodendefinitionen hinzufügen, anstatt sie in Implementierungen zu duplizieren und sie in eine abstrakte Klasse umzuwandeln .

Sie können sogar eine abstrakte Klasse haben, in der alle Methoden implementiert sind, aber Sie markieren sie als abstrakt, um anzuzeigen, dass sie nicht im Istzustand ohne Unterklassen verwendet werden soll.

Hinweis: Interface- und Abstract-Klasse unterscheiden sich in der C ++ - Welt nicht wesentlich durch Mehrfachvererbung usw., haben jedoch in Java et al. Eine unterschiedliche Bedeutung.


Sehr gut gesagt! +1
jmort253

3

Wenn Sie aufhören, darüber nachzudenken, dreht sich alles um den Polymorphismus. Sie möchten in der Lage sein, einen Code zu schreiben, der mehr kann, als man denkt, je nachdem, was Sie übergeben.

Angenommen, wir haben eine Funktion wie den folgenden Python-Code:

function foo(objs):
    for obj in objs:
        obj.printToScreen()

class HappyWidget:
    def printToScreen(self):
        print "I am a happy widget"

class SadWidget:
    def printToScreen(self):
        print "I am a sad widget"

Das Gute an dieser Funktion ist, dass sie jede Liste von Objekten verarbeiten kann, sofern diese Objekte eine "printToScreen" -Methode implementieren. Sie können ihm eine Liste von fröhlichen Widgets, eine Liste von traurigen Widgets oder sogar eine Liste übergeben, die eine Mischung davon enthält, und die foo-Funktion wird immer noch in der Lage sein, ihre Sache richtig zu machen.

Wir verweisen auf diese Art der Einschränkung, dass eine Reihe von Methoden (in diesem Fall printToScreen) als Schnittstelle implementiert werden muss implementiert und Objekte, die alle Methoden implementieren, die Schnittstelle implementieren sollen.

Wenn wir über eine dynamische, entenartige Sprache wie Python sprechen würden, wären wir im Grunde jetzt vorbei. Das statische Typensystem von C ++ verlangt jedoch, dass wir den Objekten in unserer Funktion eine Klasse zuweisen, und es kann nur mit Unterklassen dieser Anfangsklasse gearbeitet werden.

void foo( Printable *objs[], int n){ //Please correctme if I messed up on the type signature
    for(int i=0; i<n; i++){
        objs[i]->printToScreen();
    }
}

In unserem Fall besteht der einzige Grund, warum die Printable-Klasse vorhanden ist, darin, einen Platz für die printToScreen-Methode anzugeben. Da es keine gemeinsame Implementierung zwischen den Klassen gibt, die die printToScreen-Methode implementieren, ist es sinnvoll, Printable in eine abstrakte Klasse zu verwandeln, die nur zum Gruppieren ähnlicher Klassen in einer gemeinsamen Hierarchie verwendet wird.

In C ++ sind die Konzepte für absctract-Klassen und -Schnittstellen etwas verschwommen. Wenn Sie sie besser definieren möchten, denken Sie über abstrakte Klassen nach, während Schnittstellen in der Regel die allgemeinere, sprachübergreifende Vorstellung von der Menge der sichtbaren Methoden bedeuten, die ein Objekt verfügbar macht. (Obwohl einige Sprachen wie Java den Schnittstellenbegriff verwenden, um sich direkter auf etwas wie eine abstrakte Basisklasse zu beziehen.)

Grundsätzlich geben konkrete Klassen an, wie Objekte implementiert werden, während abstrakte Klassen angeben, wie sie mit dem Rest des Codes interagieren. Um Ihre Funktionen mehr polymorph zu machen, sollten Sie versuchen, einen Zeiger auf die abstrakte Oberklasse zu erhalten, wann immer dies sinnvoll erscheint.


Was Singletons angeht, sind sie wirklich ziemlich nutzlos, da sie oft nur durch eine Gruppe von statischen Methoden oder einfachen alten Funktionen ersetzt werden können. Manchmal haben Sie jedoch eine Einschränkung, die Sie zwingt, ein Objekt zu verwenden, auch wenn Sie nicht wirklich eines verwenden möchten. Daher ist das Singleton-Muster angemessen.


Übrigens haben einige Leute vielleicht bemerkt, dass das Wort "Schnittstelle" in der Java-Sprache eine besondere Bedeutung hat. Ich denke, es ist besser, sich vorerst an die allgemeinere Definition zu halten.


1

Schnittstellen

Es ist schwierig, den Zweck eines Tools zu verstehen, das ein Problem löst, das Sie noch nie hatten. Nachdem ich mit dem Programmieren begonnen hatte, verstand ich einige Zeit lang keine Schnittstellen. Wir werden verstehen, was sie getan haben, aber ich wusste nicht, warum Sie eine verwenden möchten.

Hier ist das Problem - Sie wissen, was Sie tun möchten, aber Sie haben mehrere Möglichkeiten, dies zu tun, oder Sie können später ändern, wie Sie es tun möchten. Es wäre schön, wenn Sie die Rolle des ahnungslosen Managers spielen könnten - bellen Sie einige Befehle an und erzielen Sie die gewünschten Ergebnisse, ohne sich darum zu kümmern, wie es gemacht wird.

Angenommen, Sie haben eine winzige kleine Website und speichern alle Informationen Ihrer Benutzer in einer CSV-Datei. Nicht die raffinierteste Lösung, aber sie funktioniert gut genug, um die Benutzerdaten Ihrer Mutter zu speichern. Später hebt Ihre Site ab und Sie haben 10.000 Benutzer. Vielleicht ist es Zeit, eine richtige Datenbank zu verwenden.

Wenn Sie anfangs klug gewesen wären, hätten Sie dies kommen sehen und nicht die Aufrufe getätigt, um direkt in csv zu speichern. Stattdessen würden Sie sich überlegen, wozu Sie es benötigen, unabhängig davon, wie es implementiert wurde. Sagen wir store()und retrieve(). Sie erstellen eine PersisterSchnittstelle mit abstrakten Methoden für store()und retrieve()und erstellen eine CsvPersisterUnterklasse, die diese Methoden tatsächlich implementiert.

Später können Sie eine erstellen DbPersister, die das Speichern und Abrufen von Daten ganz anders implementiert, als es Ihre CSV-Klasse getan hat.

Das Tolle ist, alles, was Sie jetzt tun müssen, ist Veränderung

Persister* prst = new CsvPersister();

zu

Persister* prst = new DbPersister();

und dann bist du fertig. Ihre Anrufe an prst.store()und prst.retrieve()werden immer noch funktionieren, sie werden nur "hinter den Kulissen" anders gehandhabt.

Jetzt mussten Sie noch die CVS- und DB-Implementierungen erstellen, sodass Sie den Luxus, der Chef zu sein, noch nicht erlebt haben. Die wirklichen Vorteile werden deutlich, wenn Sie Schnittstellen verwenden, die von einer anderen Person erstellt wurden. Wenn jemand anderes so freundlich war, ein CsvPersister()und DbPersister()bereits zu erstellen , müssen Sie nur eines auswählen und die erforderlichen Methoden aufrufen. Wenn Sie sich entscheiden, den anderen später oder in einem anderen Projekt zu verwenden, wissen Sie bereits, wie es funktioniert.

Ich bin auf meinem C ++ sehr verrostet, daher verwende ich nur einige allgemeine Programmierbeispiele. Container sind ein gutes Beispiel dafür, wie Schnittstellen Ihr Leben erleichtern.

Sie können haben Array, LinkedList, BinaryTreeusw. alle Subklassen von Containerdenen hat Methoden wie insert(), find(), delete().

Wenn Sie nun etwas in die Mitte einer verknüpften Liste einfügen, müssen Sie nicht einmal wissen, was eine verknüpfte Liste ist. Sie rufen einfach an myLinkedList->insert(4)und es durchläuft die Liste auf magische Weise und steckt sie dort ab. Selbst wenn Sie wissen, wie eine verknüpfte Liste funktioniert (was Sie wirklich sollten), müssen Sie ihre spezifischen Funktionen nicht nachschlagen, da Sie wahrscheinlich bereits wissen, wozu sie eine andere Containerfrüher verwenden.

Abstrakte Klassen

Abstrakte Klassen sind ziemlich ähnlich wie Schnittstellen (auch technisch Schnittstellen sind abstrakte Klasse, aber hier ich meine Basisklassen , die einen Teil ihrer Methoden konkretisiert haben.

Angenommen, Sie erstellen ein Spiel und müssen feststellen, ob sich Gegner in Reichweite des Spielers befinden. Sie können eine Basisklasse Enemymit einer Methode erstellen inRange(). Obwohl es bei den Feinden viele Unterschiede gibt, ist die Methode zur Überprüfung ihrer Reichweite konsistent. Daher wird Ihre EnemyKlasse eine detaillierte Methode zur Überprüfung der Reichweite haben, aber rein virtuelle Methoden für andere Dinge, die keine Ähnlichkeiten zwischen den feindlichen Typen aufweisen.

Das Schöne daran ist, wenn Sie den Bereichserkennungscode durcheinander bringen oder ihn optimieren möchten, müssen Sie ihn nur an einer Stelle ändern.

Natürlich gibt es viele andere Gründe für Interfaces und abstrakte Basisklassen, aber dies sind einige Gründe, warum Sie sie möglicherweise verwenden.

Singletons

Ich benutze sie gelegentlich, und ich bin nie von ihnen verbrannt worden. Das heißt nicht, dass sie mein Leben nicht irgendwann ruinieren werden, basierend auf den Erfahrungen anderer Leute.

Hier ist eine gute Diskussion über den globalen Staat von erfahreneren und vorsichtigeren Leuten: Warum ist der globale Staat so böse?


1

Im Tierreich gibt es verschiedene Tiere, die Säugetiere sind. Hier ist Säugetier eine Basisklasse und verschiedene Tiere leiten sich davon ab.

Haben Sie jemals ein Säugetier gesehen, das vorbeiging? Ja, ich bin mir oft sicher - aber es waren alle Arten von Säugetieren, nicht wahr?

Sie haben noch nie etwas gesehen, das buchstäblich nur ein Säugetier war. Sie waren alle Arten von Säugetieren.

Das Klassensäugetier muss verschiedene Merkmale und Gruppen definieren, existiert jedoch nicht als physikalische Einheit.

Daher ist es eine abstrakte Basisklasse.

Wie bewegen sich Säugetiere? Gehen, schwimmen, fliegen sie usw.?

Auf Säugetierebene gibt es keine Möglichkeit zu wissen, aber alle Säugetiere müssen sich irgendwie bewegen (sagen wir, dies ist ein biologisches Gesetz, um das Beispiel zu vereinfachen).

Daher ist MoveAround () eine virtuelle Funktion, da jeder Säuger, der von dieser Klasse abstammt, sie anders implementieren muss.

Da jedoch jedes Säugetier MoveAround definieren MUSS, müssen sich alle Säugetiere bewegen, und es ist unmöglich, dies auf Säugetierebene zu tun. Es muss von allen untergeordneten Klassen implementiert werden, hat aber in der Basisklasse keine Bedeutung.

MoveAround ist daher eine rein virtuelle Funktion.

Wenn Sie eine ganze Klasse haben, die Aktivität zulässt, aber nicht in der Lage ist, auf oberster Ebene zu definieren, wie dies geschehen soll, sind alle Funktionen rein virtuell und dies ist eine Schnittstelle.
Zum Beispiel - wenn wir ein Spiel haben, in dem Sie einen Roboter codieren und mir zum Kämpfen auf einem Schlachtfeld vorlegen, muss ich die Funktionsnamen und Prototypen kennen, die aufgerufen werden sollen. Es ist mir egal, wie Sie es auf Ihrer Seite implementieren, solange die "Schnittstelle" klar ist. Daher kann ich Ihnen eine Interface-Klasse zur Verfügung stellen, aus der Sie Ihren Killer-Roboter schreiben können.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.