Dies ist der zweite und letzte Teil meines AbkĂŒrzungs-Spickzettel, den ein C ++ - Entwickler kennen sollte. C ++ wird hier nur erwĂ€hnt, weil ich den Spickzettel hauptsĂ€chlich fĂŒr mich selbst erstellt habe, aber ich bin genau der gleiche C ++ - Entwickler.
TatsĂ€chlich enthĂ€lt dieser Teil Konzepte, deren Umfang nicht auf C ++ beschrĂ€nkt ist. Daher kann die Auswahl fĂŒr ein breiteres Publikum von Interesse sein.

Wie im
ersten Teil werden AbkĂŒrzungen gruppiert, wenn dies sinnvoll ist. Wenn es keinen Sinn macht, werden sie alphabetisch aufgelistet.
ParallelitĂ€t und atomare Operationen:âą
CASâą
ABAâą
FAAâą
RCUDatenspeicherung:âą
SĂUREâą
GAPâą
PACELCâą
BASISPrinzipien der Softwareentwicklung:âą
TROCKENâą
KUSSâą
YAGNIâą
NIHâą
FTSEâą
GRASPâą
FESTSonstiges:âą
ABIâą
KUHâą
FBC, FBCPâą
LRUParallelitÀt und atomare Operationen
Cas
Vergleichen und tauschen. Vergleich mit dem Austausch. Dies ist eine atomare Anweisung mit drei Argumenten: atomare Variable oder Speicheradresse, erwarteter Wert, neuer Wert. Wenn und nur wenn der Wert der Variablen mit dem erwarteten Wert ĂŒbereinstimmt, erhĂ€lt die Variable einen neuen Wert und die Anweisung wird erfolgreich abgeschlossen.
CAS gibt entweder einfach einen booleschen Wert zurĂŒck (und kann dann als Vergleichen und Setzen bezeichnet werden), oder wenn dies fehlschlĂ€gt, gibt es auch den aktuellen Wert des ersten Arguments zurĂŒck.
Pseudocodebool cas(int* addr, int& expected, int new_value) { if (*addr != expected) { expected = *addr; return false; } *addr = new_value; return true; }
In C ++ wird
CAS durch die Methodenfamilien
std::atomic<T>::compare_exchange_weak
,
std::atomic<T>::compare_exchange_strong
und die freien Funktionen
std::atomic_compare_exchange_weak
,
std::atomic_compare_exchange_strong
. Der Unterschied zwischen
* schwach und
* stark besteht darin, dass erstere falsch negative Ergebnisse liefern können. Das heiĂt, Wenn der Wert erwartet wird, geben sie
false
und ersetzen ihn nicht durch einen neuen. Der Grund fĂŒr das Vorhandensein von
* schwachen Operationen ist, dass auf einigen Architekturen
* stark relativ teuer ist. In den meisten FĂ€llen drehen sich
CAS- Anweisungen in einer Schleife (der sogenannten CAS-Schleife). Die Verwendung von
* schwach statt
* stark Àndert also nichts an der Logik, kann jedoch die Leistung verbessern.
CAS- Anweisungen werden verwendet, um Synchronisationsprimitive (wie Mutexe und Semaphoren) und sperrfreie Algorithmen zu implementieren. FĂŒhren oft zu einem
ABA- Problem.
Lesen Sie mehr:
einmal (Russisch) ,
zwei (Englisch)ABA
ABA-Problem. ABA-Problem. Dieses Problem tritt bei parallelen Algorithmen auf, die auf Vergleichen mit Austauschen basieren (siehe
CAS ), beispielsweise bei sperrfreien Algorithmen. Die Quintessenz ist, dass der Thread den Wert der atomaren Variablen liest, etwas anderes tut und diese Variable durch Vergleich mit dem Austausch aktualisiert. Das heiĂt, Die Ablauflogik lautet wie folgt: Wenn die Variable noch den vorherigen Wert enthĂ€lt, hat sich nichts geĂ€ndert, alles ist in Ordnung. Dies kann aber nicht so sein. Eine formellere Beschreibung des Problems:
- Thread 1 liest den Wert der Variablen, er ist gleich A.
- Thread 1 wird herausgedrĂŒckt, Thread 2 startet
- Thread 2 Ă€ndert den Wert der Variablen von A nach B, nimmt eine Reihe von Ănderungen vor (Ă€ndert einen der Variablen zugeordneten Wert oder gibt nur Speicher frei) und Ă€ndert dann erneut den Wert - von B nach A.
- Thread 1 nimmt den Betrieb wieder auf, vergleicht den zuvor erhaltenen Wert mit dem aktuellen und kommt zu dem Schluss, dass sich nichts geÀndert hat
Mögliche Lösungen fĂŒr das Problem:
- Am einfachsten und offensichtlichsten ist die Verwendung von Schlössern. Dies fĂŒhrt zu dem ĂŒblichen thread-sicheren Algorithmus mit kritischen Abschnitten. Aber es wird aufhören, frei von Schlössern zu sein. Wenn es jedoch um CAS und ABA geht , ist dies höchstwahrscheinlich keine Option.
- FĂŒgen Sie den verglichenen Werten spezielle Beschriftungen hinzu. Zum Beispiel ein ZĂ€hler fĂŒr die Anzahl der Ănderungen. Einerseits kann dieser ZĂ€hler ĂŒberlaufen, andererseits unterstĂŒtzen moderne x86_64-Prozessoren 128-Bit- CAS- Operationen. Das heiĂt, Wenn Sie Zeiger mit einem ZĂ€hler vergleichen, können Sie bis zu 64 Bit angeben, und jemand hat festgestellt, dass dies fĂŒr 10 Jahre kontinuierlichen Betrieb des Algorithmus ausreicht.
- Einige Architekturen (z. B. ARM) bieten LL / SC-Anweisungen (Load Linked, Store Conditional), mit denen Sie nicht nur den aktuellen Wert der Adresse im Speicher abrufen, sondern auch nachvollziehen können, ob sich dieser Wert seit dem letzten Lesen geÀndert hat.
Um Datenstrukturen wie einen Stapel, eine Liste oder eine Warteschlange zu verwenden, die im Allgemeinen nicht blockiert werden können, wenn die Gefahr besteht, dass ein hĂ€ngender Zeiger auf einen Remote-Knoten verbleibt, gibt es eine ganze Familie von Lösungen fĂŒr das
ABA- Problem, die auf dem verzögerten Entfernen von Knoten basieren. Dies umfasst den Garbage Collector, Hazard Pointers und den Read-Modify-Write-Mechanismus (siehe
RCU ).
Lesen Sie mehr:
eins (Russisch) ,
zwei (Englisch) ,
drei (Englisch)FAA
Holen und hinzufĂŒgen. Ăhm ... holen und hinzufĂŒgen (es scheint, dass dieses Konzept nie ins Russische ĂŒbersetzt wird). Eine atomare Operation mit zwei Argumenten: einer atomaren Variablen oder einer Adresse im Speicher und dem Wert, um den diese Variable geĂ€ndert werden muss. Wenn die Architektur dies zulĂ€sst, gibt die Operation den vorherigen Wert der geĂ€nderten Variablen zurĂŒck (x86 erlaubt seit i486). Im Gegensatz zu
CAS ist die
FAA immer erfolgreich.
Pseudocode int faa(int* addr, int diff) { int value = *addr; *addr = value + diff; return value; }
In C ++ wird es als die Familien der Methoden
std::atomic<T>::fetch_add
,
fetch_sub
,
fetch_and
,
fetch_or
,
fetch_xor
und die entsprechenden freien Funktionen
std::atomic_fetch_add
usw.
std::atomic_fetch_add
.
Wie es sich fĂŒr eine atomare Anweisung gehört, wird die
FAA bei der Implementierung von Synchronisationsprimitiven und sperrfreien Algorithmen und Datenstrukturen verwendet.
Lesen Sie mehr:
einmal (Russisch) ,
zwei (Englisch)RCU
Read-Copy-Update. Lesen-Ăndern-Schreiben. Dies ist ein nicht blockierender Mechanismus zum Synchronisieren des Zugriffs auf die Datenstruktur (natĂŒrlich sperrenfrei). Es wird in FĂ€llen verwendet, in denen die Lesegeschwindigkeit kritisch ist. Es ist ein Beispiel fĂŒr den Kompromiss zwischen Zeit und GedĂ€chtnis (Raum-Zeit-Kompromiss).
Die Idee von
RCU ist, dass der Writer-Stream die Daten nicht an Ort und Stelle Ă€ndert, sondern eine Kopie erstellt, die erforderlichen Ănderungen daran vornimmt und die aktuellen Daten und die geĂ€nderte Kopie atomar austauscht. Gleichzeitig haben Leser-Threads stĂ€ndig Zugriff auf Daten - alte oder neue, wer auch immer Zeit hat. Wenn keine Leser mehr mit der veralteten Version arbeiten, löscht der Schreiber Daten, die nicht mehr benötigt werden, wodurch Speicherplatz frei wird.
Sehr vereinfachte RCU funktioniert folgendermaĂen:
- Viele Leser, ein Schriftsteller.
- Lesen und Ăndern erfolgen gleichzeitig.
- Leser verwenden eine sehr leichte Synchronisation. TatsÀchlich muss der Leser den Verfasser nur beim Betreten des kritischen Abschnitts und beim Verlassen des kritischen Abschnitts benachrichtigen. Die Arbeit mit synchronisierten Daten erfolgt nur im kritischen Bereich.
- Sobald der Verfasser die Daten atomar durch eine Kopie ersetzt, kĂŒndigt er den Beginn der sogenannten Nachfrist (Nachfrist) an. Die Nachfrist endet, wenn alle Leser, die sich zu Beginn dieser Frist in kritischen Abschnitten befanden, ihre kritischen Abschnitte verlassen haben. Jetzt kann der Writer veraltete Daten sicher löschen. Es wird vorausgesetzt, dass alle kritischen Abschnitte endlich sind, was die Endlichkeit der Nachfrist garantiert.
RCU eignet sich hervorragend fĂŒr Daten, die hĂ€ufig gelesen und selten aktualisiert werden. Dieser Mechanismus wird im Linux-Kernel aktiv verwendet, wo es recht einfach ist, festzustellen, wann die Kulanzfrist endet.
Nachteile:
- Schlecht fĂŒr die Synchronisierung des Zugriffs auf hĂ€ufig geĂ€nderte Daten.
- Im Benutzerbereich schwer zu implementieren.
- HÀngt von der FÀhigkeit ab, Zeiger auf eine Adresse im Speicher atomar zu Àndern, aber nicht alle Architekturen bieten diese FÀhigkeit.
Lesen Sie mehr:
einmal (Russisch) ,
zwei (Englisch)Datenspeicherung
SĂURE
AtomizitÀt, Konsistenz, Isolation, Haltbarkeit. AtomizitÀt, Konsistenz, Isolation, Haltbarkeit. Dies ist eine Reihe von Transaktionsanforderungen in einem DBMS.
ACID bietet auch bei Fehlern einen zuverlÀssigen und vorhersehbaren DBMS-Betrieb.
- Atomicity stellt sicher, dass die Transaktion entweder vollstÀndig abgeschlossen ist oder nichts tut. Ein Zwischenzustand ist unmöglich, es wird nicht so sein, dass eine Transaktionsoperation erfolgreich war und die andere nicht. Alles oder nichts.
- Die Konsistenz stellt sicher, dass alle Daten in der Datenbank alle angegebenen Regeln und EinschrĂ€nkungen sowohl vor dem Start der Transaktion als auch nach deren Abschluss erfĂŒllen. WĂ€hrend der AusfĂŒhrung der Transaktion kann die Konsistenz verletzt werden.
- Durch die Isolierung wird sichergestellt, dass sich gleichzeitige Transaktionen nicht gegenseitig beeinflussen. Keine Transaktion hat Zugriff auf inkonsistente Daten, die von einer anderen Transaktion verarbeitet werden.
- Haltbarkeit bedeutet, dass das Ergebnis einer erfolgreichen Transaktion in der Datenbank gespeichert wird und nicht verloren gehen kann, unabhÀngig davon, was unmittelbar nach Abschluss der Transaktion mit der Datenbank geschieht.
Alle wichtigen relationalen DBMS unterstĂŒtzen
ACID vollstĂ€ndig. In der NoSQL-Welt ist eine solche vollstĂ€ndige UnterstĂŒtzung eher eine Ausnahme.
Lesen Sie mehr: einmal
(Englisch) ,
zwei (Englisch)Cap
CAP-Theorem. CAP-Theorem. Der Satz besagt, dass jedes verteilte System nicht mehr als zwei Eigenschaften aus der Liste haben kann: Datenkonsistenz (
Konsistenz ), VerfĂŒgbarkeit (
VerfĂŒgbarkeit ), BestĂ€ndigkeit gegen Trennung (
Partitionstoleranz ).
- Konsistenz bedeutet in diesem Fall (vereinfachte) konsistente Konsistenz. Das heiĂt, Sobald der Datenaktualisierungsvorgang auf einem Knoten erfolgreich abgeschlossen wurde, verfĂŒgen alle anderen Knoten bereits ĂŒber diese aktualisierten Daten. Dementsprechend befinden sich alle Knoten in einem konsistenten Zustand. Dies ist nicht die von ACID geforderte Konsistenz.
- VerfĂŒgbarkeit bedeutet, dass jeder ausgefallene Knoten in angemessener Zeit eine korrekte Antwort auf jede Anforderung (sowohl zum Lesen als auch zum Schreiben) zurĂŒckgibt. Es gibt keine Garantie dafĂŒr, dass die Antworten von verschiedenen Knoten ĂŒbereinstimmen.
- Trennungswiderstand bedeutet, dass das System bei Verlust einer beliebigen Anzahl von Nachrichten zwischen seinen Knoten weiterhin ordnungsgemÀà funktioniert.
Weil Alle drei Eigenschaften sind nicht erreichbar. Aus Sicht des
CAP- Theorems fallen alle verteilten Systeme in drei Klassen:
CA ,
CP und
AP .
CA- Systeme haben offensichtlich keinen Trennwiderstand. Weil In den allermeisten FĂ€llen impliziert die Verteilung die Verteilung ĂŒber ein reales Netzwerk, und in einem realen Netzwerk besteht immer eine Wahrscheinlichkeit ungleich Null, dass ein Paket verloren geht. Dann sind
CA- Systeme von geringem Interesse.
Die Wahl liegt zwischen
CP und
AP , d. H. Zwischen Konsistenz und VerfĂŒgbarkeit. Herkömmliche relationale DBMS, die den
ACID- Prinzipien folgen, bevorzugen Konsistenz. WÀhrend viele NoSQL-Lösungen Barrierefreiheit und
BASE wÀhlen.
Im Falle eines normalen Betriebs des Netzwerks, d. H. Wenn keine Netzwerktrennung vorliegt, legt der
CAP- Satz keine EinschrĂ€nkungen fĂŒr die Konsistenz und VerfĂŒgbarkeit fest. Das heiĂt, etwas zu spenden ist nicht notwendig.
Lesen Sie mehr:
eins (Russisch) ,
zwei (Englisch) ,
drei (Englisch)Pacelc
PACELC-Theorem. PACELC-Theorem. Dies ist eine Erweiterung des
CAP- Theorems, das besagt, dass ein verteiltes System bei einer Netzwerkpartition (
Partition ) gezwungen ist, zwischen
VerfĂŒgbarkeit (
Konsistenz ) und bei normalem Netzwerkbetrieb (
sonst ) zwischen Latenz (
Konsistenz) zu wÀhlen. )
Wenn der
CAP- Satz zwei Klassen von Systemen unterscheidet, die bei der Netzwerktrennung stabil sind, hat
PACELC dementsprechend vier davon:
PA / EL ,
PA / EC ,
PC / EL und
PC / EC . Einige NoSQL-Datenbanken können ihre Klasse abhÀngig von den Einstellungen Àndern.
Lesen Sie mehr:
einmal (Russisch) ,
zwei (Englisch)BASIS
GrundsĂ€tzlich verfĂŒgbar, weicher Zustand, eventuelle Konsistenz. GrundverfĂŒgbarkeit, fragiler Zustand, Konsistenz auf lange Sicht. Nach dem
CAP- Theorem in verteilten Systemen muss etwas aufgegeben werden. Auf lange Sicht wird die strikte KohÀrenz normalerweise zugunsten der KohÀrenz aufgegeben. Dies bedeutet, dass das System eines Tages, wenn keine DatenÀnderungen vorgenommen werden, irgendwann einen konsistenten Zustand erreicht.
Um auf einen solchen Kompromiss hinzuweisen, wurde die AbkĂŒrzung
BASE etwas
enger verwendet , und es stellte sich ein Spiel chemischer Begriffe heraus (
ACID - AciditÀt,
BASE - BasizitÀt).
- GrundsĂ€tzlich verfĂŒgbar bedeutet, dass das System die DatenverfĂŒgbarkeit garantiert und auf jede Anfrage reagiert. Die Antwort kann jedoch veraltete oder inkonsistente Daten sein (oder deren Fehlen).
- Weicher Zustand bedeutet, dass sich der Zustand des Systems im Laufe der Zeit Àndern kann, auch wenn keine DatenÀnderungen angefordert werden. Denn zu jedem Zeitpunkt können Daten in einen konsistenten Zustand gebracht werden.
- Eventuelle Konsistenz bedeutet, dass die Daten, wenn sie sich nicht mehr Ă€ndern, natĂŒrlich in einem konsistenten Zustand enden. Das heiĂt, Dieselbe Anfrage an verschiedene Knoten fĂŒhrt zu denselben Antworten.
Lesen Sie mehr: einmal
(Englisch) ,
zwei (Englisch)Prinzipien der Softwareentwicklung
TROCKEN
Wiederholen Sie sich nicht. Nicht wiederholen. Dies ist das Prinzip der Softwareentwicklung, dessen Hauptidee darin besteht, die Menge doppelter Informationen im System zu reduzieren. Ziel ist es, die KomplexitÀt des Systems zu verringern und seine Verwaltbarkeit zu verbessern.
Im Original (
The Pragmatic Programmer Book, von
Andrew Hunt und
David Thomas ) wird dieses Prinzip wie folgt formuliert: âJedes Wissen sollte eine einzige, konsistente und maĂgebliche Darstellung innerhalb des Systems haben.â In diesem Fall bedeutet Wissen jeden Teil eines Themenbereichs oder Algorithmus: einen Code, ein Datenbankschema, ein bestimmtes Interaktionsprotokoll usw. Um eine Ănderung am System vorzunehmen, muss nur ein âWissenâ an einer Stelle aktualisiert werden.
Ein dummes Beispiel: Client und Server ĂŒbertragen strukturierte Daten untereinander. Weil Dies sind verschiedene Anwendungen, die auf verschiedenen Maschinen ausgefĂŒhrt werden. Beide mĂŒssen ihre eigenen Implementierungen dieser Strukturen haben. Wenn sich etwas Ă€ndert, mĂŒssen Ănderungen an zwei Stellen vorgenommen werden. Ein naheliegender Schritt, um diese Wiederholung zu vermeiden, besteht darin, den gemeinsamen Code einer separaten Bibliothek zuzuweisen. Der nĂ€chste Schritt besteht darin, es gemÀà der Beschreibung der Strukturen (z. B. Google Protocol Buffers) zu generieren, um nicht denselben Codetyp fĂŒr den Zugriff auf die Felder der Strukturen zu schreiben.
Die VervielfÀltigung von Codes ist nur ein Sonderfall einer
DRY- Verletzung. Und das ist nicht immer der Fall. Wenn zwei Codeteile gleich aussehen, aber jeweils ihre eigene GeschÀftslogik implementieren, wird
DRY nicht beschÀdigt.
Wie jedes andere Prinzip ist
DRY ein Werkzeug, kein Dogma. Je gröĂer das System ist, desto leichter ist es, dieses Prinzip zu verletzen. Erstens ist das Ideal unerreichbar. Und zweitens, wenn das blinde Folgen von
DRY zu komplizierterem Code fĂŒhrt und das VerstĂ€ndnis erschwert, ist es besser, ihn aufzugeben.
Lesen Sie mehr:
eins (russisch) ,
zwei (russisch)KUSS
Halte es einfach, dumm. Machen Sie es einfacher (dumm in diesem Fall ist kein Anruf). Dies ist das Konstruktionsprinzip, nach dem die meisten Systeme besser funktionieren, wenn sie einfach bleiben. Das Prinzip stammt aus der Flugzeugindustrie und wird hÀufig angewendet, auch in der Softwareentwicklung.
Im letzteren Fall ist
KISS sowohl beim Entwerfen als auch beim direkten Schreiben von Code nĂŒtzlich. Einfache Architektur und Code sind nicht nur einfacher zu verstehen, sondern auch einfacher zu verwenden, zu warten und zu entwickeln. MapReduce sollte nicht getĂ€uscht werden, wenn ein Produzent-Konsumenten-Paar ausreicht. Die Magie der Metaprogrammierung ist Ă€uĂerst komplex, wenn Sie mit ein paar normalen Funktionen arbeiten und das erforderliche Leistungsniveau erreichen können.
Bei alledem darf man nicht vergessen, dass Einfachheit kein Ziel ist, sondern nur eine nicht funktionale Anforderung. Die Hauptsache ist, das Entwurfs- / Implementierungsziel zu erreichen, und es ist ratsam, dies auf möglichst einfache Weise zu tun.
Lesen Sie mehr:
eins (russisch) ,
zwei (russisch) ,
drei (englisch)YAGNI
Du wirst es nicht brauchen. Du brauchst es nicht. Dies ist das Prinzip der Softwareentwicklung, dessen Hauptidee die Ablehnung ĂŒbermĂ€Ăiger FunktionalitĂ€t ist, und das Ziel besteht darin, Ressourcen zu sparen, die fĂŒr die Entwicklung aufgewendet werden.
Laut YAGNI mĂŒssen Sie keine Funktionen entwerfen oder implementieren, die derzeit nicht benötigt werden. Auch wenn Sie sicher sind, dass sie in Zukunft benötigt werden. Sie mĂŒssen keine unnötigen Abstraktionen hinzufĂŒgen, deren Vorteile spĂ€ter erscheinen werden.
Das Problem ist, dass die Menschen die Zukunft nicht gut vorhersagen. Daher wird höchstwahrscheinlich alles, was "in Reserve" gemacht wird, einfach nicht nĂŒtzlich sein. Und es stellt sich heraus, dass die Zeit und das Geld, die fĂŒr solche Entwicklungen, Tests und Dokumentationen aufgewendet werden, verschwendet werden. AuĂerdem ist die Software komplizierter geworden, und es mĂŒssen zusĂ€tzliche Ressourcen aufgewendet werden, um sie erneut zu unterstĂŒtzen. Schlimmer noch, wenn sich herausstellt, dass es notwendig war, etwas anderes zu tun. Geld und Zeit werden auch fĂŒr die Korrektur aufgewendet.
Das
YAGNI- Prinzip ist
etwas radikaler als
DRY und
KISS . Wenn sie das System in verstÀndliche Teile
zerlegen und Entscheidungen einfach treffen, schneidet
YAGNI einfach unnötige Teile und Lösungen aus.
Lesen Sie mehr:
eins (Russisch) ,
zwei (Englisch) ,
drei (Englisch)Nih
Hier nicht erfunden. Hier nicht erfunden. Dies ist ein Syndrom der Ablehnung der Entwicklungen anderer Menschen, eine Position, die praktisch an die Erfindung eines Fahrrads grenzt. Oft geht das Syndrom mit der Ăberzeugung einher, dass die Entwicklung von Technologie im Unternehmen sowohl schneller als auch billiger ist und die Technologie selbst die Anforderungen des Unternehmens besser erfĂŒllt. In den meisten FĂ€llen ist dies jedoch nicht der Fall, und
NIH ist ein Anti-Muster.
Hier sind einige FĂ€lle, die
NIH rechtfertigen:
- Die QualitÀt der Drittanbieterlösung ist nicht hoch genug oder der Preis ist nicht niedrig genug.
- Eine Drittanbieterlösung unterliegt LizenzbeschrÀnkungen.
- Die Verwendung einer Drittanbieterlösung fĂŒhrt zu einer AbhĂ€ngigkeit von ihrem Lieferanten und gefĂ€hrdet somit das GeschĂ€ft.
Lesen Sie mehr:
eins (Russisch) ,
zwei (Englisch) ,
drei (Englisch)Ftse
Grundsatz der Softwareentwicklung. Grundsatz der Softwareentwicklung. Eigentlich ist dies kein Satz, es gibt keinen Beweis. Dies ist ein berĂŒhmtes Sprichwort von Andrew Koenig:
Jedes Problem kann durch HinzufĂŒgen einer weiteren Abstraktionsebene gelöst werden.
Manchmal fĂŒgen sie diesem Satz hinzu: "... mit Ausnahme des Problems zu vieler Abstraktionsebenen." Im Allgemeinen ist der âSatzâ keine ernste Sache, aber es lohnt sich, darĂŒber Bescheid zu wissen.
Lesen Sie mehr: einmal
(Englisch) ,
zwei (Englisch)GRASP
Allgemeine Software-Muster fĂŒr die Zuweisung von Verantwortlichkeiten. Vorlagen fĂŒr die allgemeine ZustĂ€ndigkeitszuweisung. Diese neun Muster werden in
Anwenden von UML und Mustern von
Craig Larman formuliert. Jede Vorlage ist eine typische Lösung fĂŒr ein (aber eher allgemeines) Software-Designproblem.
- Informationsexperte . Problem: Was ist das allgemeine Prinzip der Verteilung der Verantwortlichkeiten zwischen Objekten? Entscheidung: Weisen Sie jemandem eine Pflicht zu, der ĂŒber die zur ErfĂŒllung dieser Verpflichtung erforderlichen Informationen verfĂŒgt.
- Schöpfer Problem: Wer sollte fĂŒr die Erstellung eines neuen Objekts verantwortlich sein? Lösung: Klasse B muss Instanzen der Klasse A erstellen, wenn eine oder mehrere der folgenden Bedingungen erfĂŒllt sind:
- Klasse B aggregiert oder enthÀlt Instanzen von A.
- B schreibt A.
- B verwendet aktiv A.
- B hat Initialisierungsdaten A. - Niedrige Kupplung Problem: Wie können die Auswirkungen von VerĂ€nderungen verringert werden? Wie kann die Möglichkeit der Wiederverwendung erhöht werden? Lösung: Verteilen Sie die Verantwortlichkeiten so, dass die KonnektivitĂ€t gering ist. Die Kopplung ist ein MaĂ dafĂŒr, wie starr die Elemente miteinander verbunden sind und wie stark sie voneinander abhĂ€ngen. Das heiĂt, Es wird empfohlen, Objekte so zu verbinden, dass sie nur das erforderliche Minimum voneinander kennen.
- Hohes Getriebe ( hohe KohÀsion ). Problem: Wie gehe ich mit KomplexitÀt um? Lösung: Verteilen Sie die Verantwortlichkeiten so, dass ein hohes Engagement erhalten bleibt. Ein hohes Engagement bedeutet, dass sich die Verantwortlichkeiten eines Elements auf einen Bereich konzentrieren.
- Controller Problem: Wer sollte fĂŒr die Behandlung von Eingabeereignissen verantwortlich sein? Lösung: Weisen Sie eine verantwortliche Klasse zu, die entweder das gesamte System oder Subsystem als Ganzes (externer Controller) oder ein bestimmtes Skript (Skript oder Sitzungscontroller) darstellt. Gleichzeitig implementiert der Controller keine Reaktion auf Ereignisse, sondern delegiert diese an die entsprechenden AusfĂŒhrenden.
- Polymorphismus ( Polymorphismus ). Problem: Wie gehe ich je nach Typ mit unterschiedlichen Verhaltensweisen um? Lösung: Wenn das Verhalten vom Typ abhĂ€ngt, weisen Sie den Typ, der dieses oder jenes Verhalten implementiert, als verantwortlich zu, und greifen Sie ĂŒber eine verallgemeinerte Schnittstelle auf die Typen zu.
- ( Pure Fabrication ). : , ? : , .
- ( Indirection ). : , Low Coupling ? : .
- ( Protected Variations ). : , ? : , .
GRASP SOLID . , . . â .
:
(.) ,
(.) ,
(.)SOLID
SOLID. - ( ). (Robert Martin) 2000-. â , , .
- ( Single Responsibility Principle, SRP ). . « , ».
- / ( Open Closed Principle, OCP ). (, , ) , . : , .
- ( Liskov Substitution Principle, LSP ). , . Das heiĂt, - .
- ( Interface Segregation Principle, ISP ). , . , . , , . , .
- ( Dependency Inversion Principle, DIP ). . . . . , , , , ( Java C#).
:
(.) ,
(.) ,
(.)Andere
ABI
Application Binary Interface. . , ( , , ).
ABI â , (, ).
ABI ELF Linux PE Windows. , (, , etc.) . , ELF PE , .
ABI , , , , , . . . .
C++
ABI , , . . . , C++ Unix- (Linux, FreeBSD, MacOS) x86_64
System V AMD64 ABI , ARM â
ARM C++ ABI . Visual C++ ABI , . System V ABI,
(mangling)
( Linux 6 , Windows â 4), .
API
ABI , ,
. C++11 ( ). - GCC 5 (
COW ), .
:
(.) ,
(.) .
COW
Beim Schreiben kopieren. Bei Aufnahme kopieren. Dies ist ein Ressourcenverwaltungsmechanismus, der auch als implizite Freigabe und verzögertes Kopieren bezeichnet wird. Die Idee ist, dass, wenn eine Kopie erforderlich ist, die Ressource nicht tatsĂ€chlich kopiert wird, sondern ein Link dazu erstellt wird. Und nur wenn ĂnderungswĂŒnsche vorliegen - im Original oder in der âKopieâ - erfolgt erst dann die Erstellung einer vollstĂ€ndigen Kopie.Der Vorteil von COW liegt auf der Hand: Das Kopieren eines Objekts erfolgt sofort. Wenn Objekte hĂ€ufig kopiert, aber selten geĂ€ndert werden, können Leistungssteigerungen erheblich sein.Beispiele fĂŒr die Verwendung von COW :- Verwaltung des virtuellen Prozessspeichers unter Linux. Beim Aufruf der
fork()
Seiten wird der Prozessspeicher nicht kopiert, sondern nur als freigegeben markiert. - (snapshots) (Btrfs, ZFS) (MS SQL Server).
- ++11
std::string
COW . C++11 std::string
(. ABI ). - Qt COW .
:
(.) ,
(.)FBC, FBCP
Fragile Base Class (Problem). . , , .
, struct Base { virtual void method1() {
FBC , , Java ( C++ ).
FBCP :
- , (
final
C++ Java). - , .
- , , .
:
(.) ,
(.) ,
(.)LRU
Least Recently Used. . ( ). «-», â (hit ratio). , . , . â , , .
LRU , . , , . , .
LRU â O(n), â O(1), â O(1). - .
, template <class K, class V> class LRU { private: using Queue = std::list<std::pair<K, V>>; using Iterator = typename Queue::iterator; using Hash = std::unordered_map<K, Iterator>; Queue queue_; Hash hash_; const size_t limit_; public: LRU(size_t limit) : limit_(limit) { } std::optional<V> get(const K& key) { const auto it = hash_.find(key); if (it == hash_.end()) { return {}; } it->second = reorder(it->second); return { it->second->second }; } void add(K&& key, V&& value) { if (hash_.size() >= limit_) { pop(); } queue_.emplace_front(std::move(key), std::move(value)); const auto it = queue_.begin(); hash_[it->first] = it; } private: Iterator reorder(Iterator it) { queue_.emplace_front(std::move(it->first), std::move(it->second)); queue_.erase(it); return queue_.begin(); } void pop() { hash_.erase(queue_.back().first); queue_.pop_back(); } };
LRU , . . n .
LRU : MRU (Most Recently Used), LFU (Least Frequently Used), Segmented LRU, 2Q . .
:
(.) ,
(.) ,
(.)PS
- - â .