Ich bin ein aufstrebender Spieleentwickler, arbeite an gelegentlichen Indie-Spielen und mache seit einiger Zeit etwas, das anfangs wie eine schlechte Praxis schien, aber ich möchte wirklich eine Antwort von einigen erfahrenen Programmierern hier bekommen.
enumList.h
Angenommen , ich habe eine Datei mit dem Namen, in der ich alle Aufzählungen deklariere, die ich in meinem Spiel verwenden möchte:
// enumList.h
enum materials_t { WOOD, STONE, ETC };
enum entity_t { PLAYER, MONSTER };
enum map_t { 2D, 3D };
// and so on.
// Tile.h
#include "enumList.h"
#include <vector>
class tile
{
// stuff
};
Die Hauptidee ist, dass ich alle Aufzählungen im Spiel in einer Datei deklariere und diese Datei dann importiere, wenn ich eine bestimmte Aufzählung daraus verwenden muss, anstatt sie in der Datei zu deklarieren, in der ich sie verwenden muss. Ich mache das, weil es die Dinge sauber macht. Ich kann auf jede Aufzählung an einem Ort zugreifen, anstatt Seiten nur für den Zugriff auf eine Aufzählung öffnen zu lassen.
Ist dies eine schlechte Praxis und kann sie die Leistung in irgendeiner Weise beeinträchtigen?
quelle
materials_t
Dateien, die sich nicht mit Materialien befassen , ein Material hinzufügen, müssen Sie es neu erstellen.enumList.h
als Sammlung von#include
s für diese Dateien dienen. Dies ermöglicht Dateien, die nur eine Aufzählung benötigen, um sie direkt abzurufen, und bietet gleichzeitig ein einzigartiges Paket für alles, was wirklich alle möchte.Antworten:
Ich denke wirklich, dass es eine schlechte Praxis ist. Wenn wir die Codequalität messen, nennen wir das "Granularität". Ihre Granularität leidet stark, wenn Sie alle diese Aufzählungen in einer einzigen Datei zusammenfassen, und daher leidet auch die Wartbarkeit.
Ich hätte eine einzelne Datei pro Aufzählung, um sie schnell zu finden und sie mit dem Verhaltenscode der spezifischen Funktionalität zu gruppieren (z. B. Materialaufzählung in Ordnern, in denen sich das Materialverhalten befindet usw.).
Sie könnten denken, es ist sauber, aber in der Tat ist es nicht. Es koppelt Dinge, die funktional und modulmäßig nicht zusammengehören, und verringert die Modularität Ihrer Anwendung. Abhängig von der Größe Ihrer Codebasis und der Modularität Ihres Codes kann dies zu einem größeren Problem und zu unreinen Codes / Abhängigkeiten in anderen Teilen Ihres Systems führen. Wenn Sie jedoch nur ein kleines monolithisches System schreiben, gilt dies nicht unbedingt. Aber ich würde es selbst für ein kleines monolithisches System nicht so machen.
quelle
Ja, es ist eine schlechte Praxis, nicht wegen der Leistung, sondern wegen der Wartbarkeit.
Es macht Dinge "sauber" nur in der OCD "ähnliche Dinge zusammen sammeln". Aber das ist eigentlich nicht die nützliche und gute Art von "Sauberkeit".
Code-Entitäten sollten gruppiert werden, um den Zusammenhalt zu maximieren und die Kopplung zu minimieren. Dies wird am besten erreicht, indem sie in weitgehend unabhängige Funktionsmodule gruppiert werden. Wenn Sie sie nach technischen Kriterien gruppieren (z. B. alle Aufzählungen zusammenfügen), wird das Gegenteil erreicht: Sie koppelt Code, der absolut keine funktionale Beziehung hat, und fügt Aufzählungen, die möglicherweise nur an einer Stelle verwendet werden, in eine andere Datei ein.
quelle
Nun, das sind nur Daten (kein Verhalten). Das Schlimmste, was möglicherweise / theoretisch passieren könnte, ist, dass derselbe Code mehr als einmal enthalten und kompiliert wird, wodurch ein relativ größeres Programm erstellt wird.
Es ist einfach unmöglich, dass solche Includes Ihren Vorgängen mehr Zyklen hinzufügen können, da sie keinen Verhaltens- / Verfahrenscode enthalten (keine Schleifen, keine Wenns usw.).
Ein (relativ) größeres Programm kann die Leistung (Ausführungsgeschwindigkeit) kaum beeinträchtigen und ist auf jeden Fall nur ein entferntes theoretisches Problem. Die meisten (möglicherweise alle) Compiler verwalten Includes so, dass solche Probleme vermieden werden.
IMHO, die Vorteile, die Sie mit einer einzelnen Datei (besser lesbarer und besser verwaltbarer Code) erhalten, übertreffen jeden möglichen Nachteil bei weitem.
quelle
Für mich hängt alles vom Umfang Ihres Projekts ab. Wenn es eine Datei mit beispielsweise 10 Strukturen gibt und dies die einzigen sind, die jemals verwendet wurden, wäre es mir sehr angenehm, eine .h-Datei dafür zu haben. Wenn Sie verschiedene Arten von Funktionen haben, z. B. Einheiten, Wirtschaft, Gebäude usw., würde ich die Dinge definitiv aufteilen. Erstellen Sie eine unit.h, in der sich alle Strukturen befinden, die sich mit Einheiten befassen. Wenn Sie irgendwo etwas mit Einheiten machen möchten, müssen Sie Einheiten einschließen.h sicher, aber es ist auch eine nette "Kennung", dass etwas mit Einheiten wahrscheinlich in dieser Datei gemacht wird.
Betrachten Sie es so, Sie kaufen keinen Supermarkt, weil Sie eine Dose Cola brauchen;)
quelle
Ich bin kein C ++ - Entwickler, daher wird diese Antwort für OOA & D allgemeiner sein:
In der Regel sollten Codeobjekte nach funktionaler Relevanz gruppiert werden, nicht unbedingt nach sprachspezifischen Konstrukten. Die wichtigste Ja-Nein-Frage, die immer gestellt werden sollte, lautet: "Wird vom Endcodierer erwartet, dass er die meisten oder alle Objekte verwendet, die er beim Konsumieren einer Bibliothek erhält?" Wenn ja, gruppieren Sie sich. Wenn nicht, sollten Sie die Codeobjekte aufteilen und näher an anderen Objekten platzieren, die sie benötigen (wodurch sich die Wahrscheinlichkeit erhöht, dass der Verbraucher alles benötigt, auf das er tatsächlich zugreift).
Das Grundkonzept ist "hoher Zusammenhalt"; Codemitglieder (von Methoden einer Klasse bis hin zu Klassen in einem Namespace oder einer DLL und DLLs selbst) sollten so organisiert sein, dass ein Codierer alles enthalten kann, was er benötigt, und nichts, was er nicht benötigt. Dies macht das Gesamtdesign toleranter gegenüber Änderungen. Dinge, die sich ändern müssen, können sein, ohne andere Codeobjekte zu beeinflussen, die sich nicht ändern mussten. In vielen Fällen wird die Anwendung dadurch auch speichereffizienter. Eine DLL wird vollständig in den Speicher geladen, unabhängig davon, wie viele dieser Anweisungen jemals vom Prozess ausgeführt werden. Das Entwerfen von "schlanken" Anwendungen erfordert daher, darauf zu achten, wie viel Code in den Speicher gezogen wird.
Dieses Konzept gilt für praktisch alle Ebenen der Code-Organisation mit unterschiedlichen Auswirkungen auf Wartbarkeit, Speichereffizienz, Leistung, Erstellungsgeschwindigkeit usw. Wenn ein Codierer auf einen Header / eine DLL von eher monolithischer Größe verweisen muss, um auf ein Objekt zuzugreifen, Wenn dies nicht von einem anderen Objekt in dieser DLL abhängt, sollte die Aufnahme dieses Objekts in die DLL wahrscheinlich überdacht werden. Es ist jedoch möglich, auch in die andere Richtung zu weit zu gehen. Eine DLL für jede Klasse ist eine schlechte Idee, da sie die Erstellungsgeschwindigkeit verlangsamt (mehr DLLs müssen mit dem damit verbundenen Overhead neu erstellt werden) und die Versionierung zu einem Albtraum macht.
Ein typisches Beispiel: Wenn bei einer realen Verwendung Ihrer Codebibliothek die meisten oder alle Aufzählungen verwendet werden müssen, die Sie in diese einzelne Datei "enumerations.h" einfügen, gruppieren Sie sie auf jeden Fall. Sie werden wissen, wo Sie suchen müssen, um sie zu finden. Wenn Ihr konsumierender Codierer jedoch möglicherweise nur ein oder zwei der Dutzend Aufzählungen benötigt, die Sie in der Kopfzeile angeben, würde ich Sie ermutigen, diese in eine separate Bibliothek zu stellen und sie von den größeren Aufzählungen abhängig zu machen . Auf diese Weise können Ihre Codierer nur das eine oder andere erhalten, das sie möchten, ohne eine Verknüpfung mit der monolithischeren DLL herzustellen.
quelle
Diese Art von Dingen wird zu einem Problem, wenn mehrere Entwickler an derselben Codebasis arbeiten.
Ich habe sicherlich gesehen, dass ähnliche globale Dateien zu einem Knotenpunkt für Zusammenführungskollisionen und allerlei Trauer bei (größeren) Projekten wurden.
Wenn Sie jedoch der einzige sind, der an dem Projekt arbeitet, sollten Sie alles tun, was Ihnen am besten gefällt, und nur dann "Best Practices" anwenden, wenn Sie die Motivation dahinter verstehen (und damit einverstanden sind).
Es ist besser, einige Fehler zu machen und daraus zu lernen, als zu riskieren, für den Rest Ihres Lebens an den Programmierpraktiken des Frachtkultes festzuhalten.
quelle
Ja, es ist eine schlechte Praxis, dies bei einem großen Projekt zu tun. KUSS.
Der junge Mitarbeiter benannte eine einfache Variable in eine .h-Kerndatei um und 100 Ingenieure warteten 45 Minuten, bis alle ihre Dateien neu erstellt wurden, was sich auf die Leistung aller auswirkte. ;)
Alle Projekte beginnen im Laufe der Jahre klein und wir verfluchen diejenigen, die frühzeitig Abkürzungen für die Entstehung technischer Schulden genommen haben. Best Practices: Halten Sie globale .h-Inhalte auf das beschränkt, was unbedingt global ist.
quelle
In diesem Fall würde ich sagen, dass die ideale Antwort darin besteht, dass es davon abhängt, wie die Aufzählungen verbraucht werden. In den meisten Fällen ist es jedoch wahrscheinlich am besten, alle Aufzählungen separat zu definieren. Wenn jedoch eine der Aufzählungen bereits vom Design her gekoppelt ist, sollten Sie eine angeben Mittel zum gemeinsamen Einführen der gekoppelten Aufzählungen. Tatsächlich haben Sie eine Kopplungstoleranz bis zu dem Ausmaß der bereits vorhandenen absichtlichen Kopplung, jedoch nicht mehr.
In Anbetracht dessen ist es wahrscheinlich, dass die flexibelste Lösung wahrscheinlich jede Aufzählung in einer separaten Datei definiert, aber gekoppelte Pakete bereitstellt, wenn dies angemessen ist (wie durch die beabsichtigte Verwendung der beteiligten Aufzählungen bestimmt).
Alle Ihre Aufzählungen in der gleichen Datei Paare definieren sie zusammen, und durch die Erweiterung verursacht jeder Code, unabhängig davon , ob der Code tatsächlich auf allen Aufzählungen abhängig von einem oder mehreren Aufzählungen abhängig nutzt alle anderen Aufzählungen.
renderMap()
Ich würde viel lieber nur darüber Bescheid wissenmap_t
, da sich sonst Änderungen an den anderen darauf auswirken, obwohl sie nicht wirklich mit den anderen interagieren.In dem Fall, in dem Komponenten bereits miteinander gekoppelt sind, kann die Bereitstellung mehrerer Aufzählungen in einem einzigen Paket leicht zusätzliche Klarheit und Einfachheit bieten, vorausgesetzt, es gibt einen klaren logischen Grund für die Kopplung der Aufzählungen, und die Verwendung dieser Aufzählungen ist ebenfalls gekoppelt. und dass das Bereitstellen von ihnen auch keine zusätzlichen Kupplungen einführt.
In diesem Fall besteht keine direkte logische Verbindung zwischen Entitätstyp und Materialtyp (vorausgesetzt, die Entitäten bestehen nicht aus einem der definierten Materialien). Wenn wir jedoch einen Fall hatten, in dem beispielsweise eine Aufzählung explizit von der anderen abhängig ist, ist es sinnvoll, ein einzelnes Paket bereitzustellen, das alle gekoppelten Aufzählungen (sowie alle anderen gekoppelten Komponenten) enthält, damit die Kopplung erfolgen kann zu diesem Paket so weit wie möglich isoliert.
Aber leider ... selbst wenn zwei Aufzählungen eng miteinander verbunden sind, selbst wenn es so stark ist wie "zweite Aufzählung liefert Unterkategorien für erste Aufzählung", kann es immer noch zu einer Zeit kommen, in der nur eine der Aufzählungen erforderlich ist.
Da wir beide wissen, dass es immer Situationen geben kann, in denen das Definieren mehrerer Aufzählungen in einer einzelnen Datei zu unnötiger Kopplung führen kann und das Bereitstellen gekoppelter Aufzählungen in einem einzigen Paket die beabsichtigte Verwendung verdeutlichen und es uns ermöglichen kann, den tatsächlichen Kopplungscode selbst als zu isolieren Die ideale Lösung besteht darin, jede Aufzählung separat zu definieren und gemeinsame Pakete für alle Aufzählungen bereitzustellen, die häufig zusammen verwendet werden sollen. Die einzigen Aufzählungen, die in derselben Datei definiert sind, sind solche, die eng miteinander verbunden sind, sodass die Verwendung der einen auch die Verwendung der anderen erfordert.
quelle