Ich werde dies etwas anders angehen. Eine Kernbibliothek ist in vielen Fällen eine hervorragende Idee!
Wenn Sie zwei separate Projekte haben, sollten sich diese in zwei separaten Code-Repositorys befinden. Jetzt hängen sie von der gemeinsamen Funktionalität ab. Betrachten wir zum Beispiel Paketverarbeitungsanwendungen. Die allgemeine Funktionalität kann umfassen:
- Speicherzuordnungen
- Adressauflösungsprotokoll
- AVL-Baum
- Serialisierungscode für binäre Protokolle
- Dynamisches Array
- Hash-Liste im Linux-Kernel-Stil mit einfach verknüpftem Kopf und doppelt verknüpften mittleren Knoten
- Hash-tabelle
- TCP / IP-Header-Verarbeitungscode
- Regelmäßige verknüpfte Liste mit doppelt verknüpftem Kopf und doppelt verknüpften Mittelknoten
- Protokollierungsbibliothek
- Verschiedenes (vertrau mir, du brauchst das für kleine und triviale Dinge oder deine Anzahl an verschiedenen Modulen wird so groß wie 100 sein!)
- Paketerfassungsbibliothek
- Paket-E / A-Schnittstellenbibliothek
- Paketdatenstruktur
- Blockierwarteschlange für die Kommunikation zwischen Threads
- Zufallszahlengeneratoren
- Rot-schwarzer Baum
- Eine Art Timer-Implementierung
Nun benötigen verschiedene Paketverarbeitungsanwendungen möglicherweise eine andere Teilmenge davon. Sollten Sie eine Kernbibliothek mit einem Quellcode-Repository implementieren oder sollten Sie für jedes dieser Module 18 verschiedene Repositorys haben? Denken Sie daran, dass diese Module möglicherweise gegenseitige Abhängigkeiten aufweisen, sodass die meisten dieser Module beispielsweise von verschiedenen Modulen abhängen können.
Ich werde behaupten, dass eine Kernbibliothek der beste Ansatz ist. Es reduziert den Overhead vieler Quellcode-Repositorys. Es reduziert die Abhängigkeitshölle: Eine bestimmte Version von Speicherzuweisern benötigt möglicherweise eine bestimmte Version eines anderen Moduls. Und was ist, wenn Sie Speicherzuweiser Version 1.7 abhängig von Verschiedenes 2.5 und AVL-Baum Version 1.2 abhängig von Verschiedenes 2.6 wollen? Möglicherweise können Sie nicht gleichzeitig verschiedene 2.5 und 2.6 mit Ihrem Programm verknüpfen.
Implementieren Sie also die folgende Struktur:
- Kernbibliotheks-Repository
- Projekt # 1 Repository
- Projekt # 2 Repository
- ...
- Projekt # N Repository
Ich habe gesehen, dass der Wechsel von der Struktur zu dieser Art von Struktur:
- Projekt # 1 Repository
- Projekt # 2 Repository
- ...
- Projekt # N Repository
Hat zu weniger Wartung und mehr Code-Sharing über Nicht-Copypaste-Mechanismen geführt.
Ich habe auch Projekte mit der folgenden Struktur gesehen:
- Speicherzuweisungs-Repository
- Adressauflösungsprotokoll-Repository
- AVL-Baum-Repository
- Serialisierungscode für das Repository für binäre Protokolle
- Dynamisches Array-Repository
- Hash-Liste im Linux-Kernel-Stil mit einfach verknüpftem Kopf und doppelt verknüpftem Repository für mittlere Knoten
- Hash-Tabellen-Repository
- TCP / IP-Header-Verarbeitungscode-Repository
- Regelmäßige verknüpfte Liste mit doppelt verknüpftem Kopf und doppelt verknüpftem Mittelknoten-Repository
- Protokollierungsbibliotheks-Repository
- Verschiedenes Repository (vertrau mir, du brauchst das für kleine und triviale Dinge oder deine Anzahl an verschiedenen Modulen wird so groß wie 100 sein!)
- Repository der Paketerfassungsbibliothek
- Repository der Paket-E / A-Schnittstellenbibliothek
- Paketdatenstruktur-Repository
- Blockierungswarteschlange für das Kommunikations-Repository zwischen Threads
- Repository für Zufallszahlengeneratoren
- Rot-Schwarz-Baum-Repository
- Eine Art Timer-Implementierungs-Repository
- Projekt # 1 Repository
- Projekt # 2 Repository
- ...
- Projekt # N Repository
... und die Abhängigkeitshölle und die Verbreitung von Repository-Nummern waren echte Probleme.
Sollten Sie jetzt eine vorhandene Open Source-Bibliothek verwenden, anstatt Ihre eigene zu schreiben? Sie müssen berücksichtigen:
- Lizenzprobleme. Manchmal kann die bloße Anforderung, dem Autor in der bereitgestellten Dokumentation eine Anerkennung zu geben, zu hoch sein, da 20 Bibliotheken normalerweise 20 verschiedene Autoren haben.
- Unterstützung für verschiedene Betriebssystemversionen
- Abhängigkeiten der jeweiligen Bibliothek
- Größe der jeweiligen Bibliothek: Ist sie für die bereitgestellte Funktionalität zu groß? Bietet es zu viele Funktionen?
- Ist eine statische Verknüpfung möglich? Ist eine dynamische Verknüpfung wünschenswert?
- Ist die Schnittstelle der Bibliothek das, was Sie wollen? Beachten Sie, dass das Schreiben eines Wrappers zur Bereitstellung der gewünschten Schnittstelle in einigen Fällen einfacher sein kann als das Umschreiben der gesamten Komponente selbst.
- ... und viele, viele andere Dinge, die ich in dieser Liste nicht erwähnt habe
Normalerweise verwende ich die Regel, dass alles unter 1000 Codezeilen, was nicht etwas erfordert, das über das Fachwissen des Programmierers hinausgeht, selbst implementiert werden sollte. Hinweis: Die 1000 Zeilen enthalten Unit-Tests. Daher würde ich es auf keinen Fall empfehlen, 1000 Codezeilen selbst zu schreiben, wenn 10 000 zusätzliche Zeilen für Komponententests erforderlich sind. Für meine Paketverarbeitungsprogramme bedeutet dies, dass ich nur folgende externe Komponenten verwendet habe:
- Alles, was von einer Standard-Linux-Distribution bereitgestellt wird, da es so viele Codezeilen gibt, dass es keinen Sinn macht, Linux neu zu implementieren. Teile der Neuimplementierung von Linux würden auch über mein Fachwissen hinausgehen.
- Bison / Flex, weil das LALR-Parsen über mein Fachwissen und über 1000 Codezeilen hinausgeht. Ich könnte sicherlich selbst einen rekursiven Abstiegsparser schreiben, aber Bison / Flex sind so praktisch, dass ich sie als nützlich betrachte.
- Netmap, weil es über 1000 Zeilen sind und über mein Fachwissen hinausgehen
- Auf Überspringen von Listen basierende Timer-Implementierung von DPDK, da sie über mein Fachwissen hinausgeht, obwohl sie weniger als 1000 Codezeilen umfasst (obwohl ich alternative Timer-Implementierungen habe, die keine Überspringlisten verwenden)
Einige Dinge, die ich selbst implementiert habe, weil sie einfach sind, umfassen sogar Dinge wie:
- MurMurHash
- SipHash
- Mersenne Twister
... weil benutzerdefinierte Implementierungen davon starkes Inlining ermöglichen können, was zu einer verbesserten Leistung führt.
Ich mache keine Kryptographie; Wenn ich das tun würde, würde ich der Liste eine Art Kryptobibliothek hinzufügen, da das Schreiben von Kryptoalgorithmen für sich selbst anfällig für Cache-Timing-Angriffe sein kann, selbst wenn Sie durch gründliche Unit-Tests zeigen können, dass sie mit den offiziellen Algorithmen kompatibel sind.