Wenn ich weiterhin Code schreibe, wird es manchmal schwierig sein, den Code zu organisieren.
Dies ist Ihr Problem: Richtige Organisation, und der Stil sollte leichter fließen.
Warten Sie nicht , um Ihren Code zu organisieren: Halten Sie Ihren Code organisiert, während Sie gehen. Obwohl die Sprache dies nicht für Sie erledigt, sollte der Code dennoch in Module mit geringer Kopplung und hoher Kohäsion organisiert sein.
Diese Module liefern dann natürlich einen Namensraum. Kürzen Sie den Modulnamen (falls er lang ist) und stellen Sie den Funktionsnamen das Modul voran, um Kollisionen zu vermeiden.
Auf der Ebene der einzelnen Identifikatoren sind dies ungefähr in aufsteigender Reihenfolge der Subjektivität:
- Wähle eine Konvention und bleibe dabei
- zB
function_like_this(struct TypeLikeThis variable)
ist üblich
Vermeiden Sie auf jeden Fall die ungarische Notation (sorry JNL)
es sei denn, Sie sind bereit, es wie ursprünglich beabsichtigt zu verwenden, was bedeutet, dass Simonyis Apps nicht die Version für schreckliche Systeme sind, sondern die Notation für Apps
Warum? Ich könnte einen Aufsatz darüber schreiben, aber ich schlage stattdessen vor, dass Sie diesen Artikel von Joel Spolsky lesen und dann noch ein paar mehr suchen, wenn Sie interessiert sind. Unten finden Sie einen Link zu Simonyis Originalarbeit.
Vermeiden Sie Zeigertypedefs, es sei denn, sie sind wirklich undurchsichtige Cookie-Typen - sie verwirren nur die Dinge
struct Type *ok;
typedef struct Type *TypePtr;
TypePtr yuck;
Was meine ich mit einem undurchsichtigen Cookie-Typ ? Ich meine etwas, das in einem Modul (oder einer Bibliothek oder was auch immer) verwendet wird und an Client-Code weitergegeben werden muss, aber dieser Client-Code kann nicht direkt verwendet werden. Es wird einfach an die Bibliothek zurückgegeben.
Beispielsweise kann eine Datenbankbibliothek eine Schnittstelle wie diese verfügbar machen
/* Lots of buffering, IPC and metadata magic held in here.
No, you don't get to look inside. */
struct DBContextT;
/* In fact, you only ever get a pointer, so let's give it a nice name */
typedef struct DBContexT *DBContext;
DBContext db_allocate_context(/*maybe some optional flags?*/);
void db_release_context(DBContext);
int db_connect(DBContext, const char *connect);
int db_disconnect(DBContext);
int db_execute(DBContext, const char *sql);
Jetzt ist der Kontext für den Client-Code undurchsichtig , da Sie nicht hineinsehen können. Sie geben es einfach an die Bibliothek zurück. So etwas FILE
ist auch undurchsichtig, und ein Integer-Dateideskriptor ist auch ein Cookie , ist aber nicht undurchsichtig.
Ein Hinweis zum Design
Ich habe den Ausdruck niedrige Kopplung und hohe Kohäsion oben ohne Erklärung verwendet, und ich fühle mich ein bisschen schlecht dabei. Sie können danach suchen und wahrscheinlich einige gute Ergebnisse finden, aber ich werde versuchen, es kurz anzusprechen (wieder könnte ich einen Aufsatz schreiben, aber ich werde versuchen, es nicht zu tun).
Die oben skizzierte DB-Bibliothek weist eine geringe Kopplung auf, da sie eine kleine Schnittstelle zur Außenwelt freigibt. Durch das Ausblenden der Implementierungsdetails (teilweise mit dem undurchsichtigen Cookie-Trick) wird verhindert, dass der Client-Code von diesen Details abhängt.
Anstelle des undurchsichtigen Cookies deklarieren wir die Kontextstruktur so, dass ihr Inhalt sichtbar ist. Dazu gehört auch ein Socket-Dateideskriptor für eine TCP-Verbindung zur Datenbank. Wenn wir anschließend die Implementierung ändern, um die Verwendung eines gemeinsam genutzten Speichersegments zu unterstützen, wenn die Datenbank auf demselben Computer ausgeführt wird, muss der Client neu kompiliert und nicht nur neu verknüpft werden. Noch schlimmer ist , könnte der Client gestartet mit dem Dateideskriptor, zum Beispiel rief setsockopt
die Standardpuffergröße zu ändern, und jetzt braucht es eine Codeänderung als auch. All diese Details sollten in unserem Modul verborgen sein, wo dies praktisch ist, und dies ergibt eine geringe Kopplung zwischen den Modulen.
Das Beispiel zeigt auch eine hohe Kohäsion , da sich alle Methoden im Modul auf dieselbe Aufgabe beziehen (DB-Zugriff). Dies bedeutet , dass nur der Code , dass Bedürfnisse über die Implementierungsdetails kennen (das heißt, die Inhalte unserer Online - Cookie) tatsächlich Zugang zu ihnen haben, das erleichtert das Debuggen.
Sie können auch feststellen, dass die Auswahl eines Präfixes für die Gruppierung dieser Funktionen durch ein einzelnes Anliegen vereinfacht wurde.
Es ist einfach zu sagen, dass dieses Beispiel gut ist (zumal es noch nicht vollständig ist), aber es hilft Ihnen nicht sofort. Der Trick besteht darin, beim Schreiben und Erweitern des Codes auf Funktionen zu achten, die ähnliche Aufgaben ausführen oder mit denselben Typen arbeiten (die möglicherweise Kandidaten für ihr eigenes Modul sind), und auch auf Funktionen, die viele separate Aufgaben ausführen, die nicht erforderlich sind. Es ist wirklich verwandt und könnte Kandidaten für eine Aufteilung sein.