Welche Muster verwenden Sie, um Schnittstellen und Implementierung in C ++ zu entkoppeln?

8

Ein Problem in großen C ++ - Projekten kann Build-Zeiten sein. Es gibt einige Klassen in Ihrer Abhängigkeitsstruktur, an denen Sie arbeiten müssten, aber normalerweise vermeiden Sie dies, da jeder Build sehr lange dauert. Sie müssen nicht unbedingt die öffentliche Schnittstelle ändern, aber vielleicht möchten Sie die privaten Mitglieder ändern (eine Cache-Variable hinzufügen, eine private Methode extrahieren, ...). Das Problem, dem Sie gegenüberstehen, ist, dass in C ++ sogar private Member in der öffentlichen Header-Datei deklariert sind, sodass Ihr Build-System alles neu kompilieren muss.

Was machst du in dieser Situation?

Ich habe zwei Lösungen skizziert, die ich kenne, aber beide haben ihre Schattenseiten, und vielleicht gibt es eine bessere, an die ich noch nicht gedacht habe.

    
Tobias 09.06.2009, 23:24
quelle

6 Antworten

9

Das Pimpl-Muster:

Deklarieren Sie in Ihrer Headerdatei nur die öffentlichen Methoden und einen privaten Zeiger (den pimpl-Zeiger oder -Delegaten) für eine nach vorne deklarierte Implementierungsklasse.

Deklarieren Sie in Ihrer Quelle die Implementierungsklasse, leiten Sie jede öffentliche Methode Ihrer öffentlichen Klasse an den Delegaten weiter, und konstruieren Sie in jedem Konstruktor Ihrer öffentlichen Klasse eine Instanz Ihrer pimpl-Klasse.

Plus:

  • Ermöglicht Ihnen, die Implementierung Ihrer Klasse zu ändern, ohne alles neu kompilieren zu müssen.
  • Vererbung funktioniert gut, nur die Syntax wird etwas anders.

Minus:

  • Viele und viele dumme Methodenkörper, um die Delegation zu schreiben.
  • Irgendwie peinlich zu debuggen, da Sie viele Delegierte haben, die durchgehen müssen.
  • Ein zusätzlicher Zeiger in Ihrer Basisklasse, der ein Problem darstellen kann, wenn Sie viele kleine Objekte haben.
Tobias 09.06.2009, 23:26
quelle
6

John Lakos Large Scale C ++ - Softwaredesign ist ein ausgezeichnetes Buch, das die Herausforderungen beim Erstellen von großem C ++ anspricht Projekte. Die Probleme und Lösungen sind alle in der Realität begründet, und sicherlich wird das obige Problem ausführlich diskutiert. Sehr empfehlenswert.

    
Greg Hewgill 10.06.2009 00:08
quelle
2

Verwendung der Vererbung:

Deklarieren Sie in Ihrer Kopfzeile die öffentlichen Methoden als reine virtuelle Methoden und eine Factory.

Leiten Sie in Ihrer Quelle eine Implementierungsklasse von Ihrer Schnittstelle ab und implementieren Sie sie. In der Implementierung der Fabrik eine Instanz der Implementierung zurückgeben.

Plus:

  • Ermöglicht Ihnen, die Implementierung Ihrer Klasse zu ändern, ohne alles neu kompilieren zu müssen.
  • Einfach und idiotensicher zu implementieren.

Minus:

  • Wirklich peinlich, eine (öffentliche) abgeleitete Instanz der öffentlichen Basisklasse zu definieren, die einige der Methoden der (privaten) Implementierung der öffentlichen Basis erben sollte.
Tobias 09.06.2009 23:27
quelle
0

Sie können eine Forward-Deklaration für Klasse A verwenden, auf die durch Zeiger in einer anderen Klasse B verwiesen wird. Sie können dann die A-Headerdatei der Klasse in die Implementierungsdatei der Klasse B und nicht in ihre Headerdatei aufnehmen. Auf diese Weise wirken sich Änderungen an Klasse A nicht auf Quelldateien aus, die die Headerdatei der Klasse B enthalten. Jede Klasse, die auf Member der Klasse A zugreifen möchte, muss die Header-Datei der Klasse A enthalten.

    
Remy Lebeau 10.06.2009 00:04
quelle
0

Refactoring und pimpl / handle-body idiom verwenden, reine virtuelle Schnittstellen zu verwenden, um Implementierungsdetails zu verstecken scheint die beliebte Antwort zu sein. Bei der Entwicklung großer Systeme sollte man die Kompilierzeit und Entwicklerproduktivität in Betracht ziehen. Was aber, wenn Sie an einem bestehenden großen C ++ - System ohne Unit-Test-Abdeckung arbeiten? Refactoring kommt normalerweise nicht in Frage.

Was ich normalerweise mache, wenn ich nicht möchte, dass der Compiler die Welt kompiliert, nachdem ich einige gebräuchliche Header-Dateien berührt habe, ist ein Makefile / Skript zu haben, das nur die Dateien kompiliert, die ich neu kompilieren muss. Wenn ich beispielsweise einer Klasse eine nicht virtuelle private Funktion hinzufüge, muss nur die cpp-Datei der Klasse neu kompiliert werden, selbst wenn ihre Headerdatei von hundert anderen Dateien enthalten ist. Bevor ich den Tag verlasse, starte ich einen sauberen Build, um die Welt wieder aufzubauen.

    
Shing Yip 10.06.2009 03:01
quelle
0

Keine.

Ich sehe den Punkt bei der Verwendung eines, aber ich denke, die folgenden Argumente mildern diesen Punkt in vielen Szenarien:

  1. Klarheit kommt zuerst. Wenn kompromittierende Klarheit für die Laufzeit Geschwindigkeit muss zweimal berücksichtigt werden, was über kompromittierende Klarheit für Zeit kompilieren Geschwindigkeit?
  2. Private Mitglieder sollten sich nicht so oft ändern.
  3. Normalerweise dauert es nicht das lange, um alles neu zu erstellen.
  4. Schnellere Tools werden in Zukunft kommen, so dass das Problem der Kompiliergeschwindigkeit automatisch gemildert wird. Ihr Code wird nicht automatisch klarer.
  5. Sie sollten trotzdem häufig neu erstellen.
  6. Haben Sie Incredibuild ausprobiert?

Natürlich ist das am Ende eine wirtschaftliche Entscheidung. Wenn das Gewicht von "3" in Ihrem Projekt wichtig ist und aus irgendeinem Grund "6" nicht anwendbar ist, dann fahren Sie fort: Sie werden mehr von der Verwendung dieser Vorlagen gewinnen, als Sie verlieren.

    
Daniel Daranas 10.06.2009 07:43
quelle

Tags und Links