Ein erster Überblick: Design Patterns und Architekturmuster mit C++

Meine letzte Umfrage hat gezeigt, dass sich viele meiner Leser wünschen, dass ich über "Design und Architekturmuster mit C++" schreibe.

Lesezeit: 6 Min.
In Pocket speichern
vorlesen Druckansicht Kommentare lesen 8 Beiträge
Von
  • Rainer Grimm

Hinweis: Dieser Blogbeitrag erscheint aufgrund technischer Probleme im Backend zwei Tage verspätet.

Meine letzte Umfrage "Which mentoring program should I implement next?" ergab für mich das überraschende Ergebnis, dass sich viele meiner Leser wünschen, dass ich über "Design Patterns und Architekturmuster mit C++" schreibe. Heute möchte ich meinen Plan für die zukünftigen Beiträge vorstellen.

Obwohl ich diesen Einführungsartikel "Design Patterns und Architekturmuster mit C++" nenne, ist der Fokus dieser Artikelserie viel breiter angelegt. Ich schreibe auch über grundlegende Terminologie, Idiome und Pattern zu Concurrency. Darüber hinaus verwende ich abhängig vom Kontext die äquivalenten Begriffe Pattern und Muster.

Die Grafik verfolgt zwei Zwecke.

      1. Sie gibt einen ersten Eindruck meines Plans.
      2. Sie zeigt, welche wichtige Pattern ich eventuell vergessen habe. Wer Ergänzungen wünscht, schreibt mir gerne eine E-Mail oder – besser noch – schreibt einen Gastbeitrag.

Ich verfeinere meinen Plan nach und nach. Hier ist meine erste Verfeinerung.

Der Begriff Design Pattern geht auf Christoph Alexander zurück, der über Architektur und Stadtplanung schrieb: "Each pattern is a three part rule, which expresses a relation between a certain context, a problem, and a solution." Der Klassiker "Design Patterns: Elements of Reusable Object-Oriented Software" von Eric Gamma, Richard Helm, Ralph Johnson und John Vlissides (kurz GOF) hat diesen Begriff für die Softwareentwicklung geprägt.

Vereinfachend gesagt, gibt es drei Arten von Mustern: Architekturmuster, Entwurfsmuster und Idiome.

Architekturmuster beschreiben die grundlegende Struktur eines Softwaresystems und setzen gerne Entwurfsmuster ein. Ein Idiom ist eine Implementierung einer Architektur oder eines Entwurfsmusters in einer konkreten Programmiersprache. Diese Klassifizierung von Mustern geht auf den zweiten Klassiker zurück, der ebenfalls ein Muss ist: "Pattern-Oriented Software Architecture: A System of Patterns" Frank Buschmann, Regine Meunier, Hans Rohnert, Peter Sommerlad und Michael Stal (kurz POSA).

Wir sprechen von Patterns und Anti-Patterns. Anti-Patterns sind eine bewährte Methode, um sich selbst in den Fuß zu schießen.

Das sind nur die wichtigsten Aspekte der Terminologie zu Pattern. Ich werde aber auch weitere Eigenschaften wie ihre Vor- und Nachteile, ihre Geschichte und ihren Aufbau vorstellen.

Ich mache es kurz. Das revolutionäre, bahnbrechende Buch "Design Patterns: Elements of Reusable Object-Oriented Software" enthält 23 Muster. Sie werden auf zwei Arten klassifiziert:

      • Erzeugungsmuster, Strukturmuster und Verhaltensmuster
      • Klassenmuster und Objektmuster

Die Klassifizierung von Klassenmustern und Objektmustern ist im Wesentlichen eine Klassifizierung in Vererbung versus Komposition als Mittel zum Aufbau von Abstraktionen aus bestehenden Abstraktionen. Nicht alle der 23 Muster sind noch von großer Bedeutung. Deshalb gebe ich einen kurzen Überblick und Codebeispiele in modernem C++ zu den folgenden fettgedruckten Mustern:

Okay, die beiden Klassiker GOF (1994) und POSA (1996) sind ein wenig in die Jahre gekommen. Was bedeutet das für modernes C++? Genau mit dieser Frage beschäftige ich mich in dem nächsten Kapitel.

Ein Idiom ist eine Implementierung eines Architekturmusters oder eines Design Patterns in einer konkreten Programmiersprache. In C++ gibt es viele Idiome wie

      • Copy-and-Swap
      • The rule of zero, five, or six
      • Hidden friends
      • Resource acquisition is initialization (RAII)
      • Dynamic polymorphism and static polymorphism
      • Templates (curiously recurring template pattern (CRTP), expression templates, policy and traits, tag dispatching, type erasure, ... )

Dies ist wahrscheinlich der Teil meiner Tour durch die Pattern, bei dem ich am meisten von Kommentaren profitieren kann. Daher freue ich mich über Rückmeldungen, welche anderen Idiome in C++ ihr kennt?

Architekturmuster beschreiben die grundlegende Struktur eines Softwaresystems und setzen oft Entwurfsmustern ein. Ich werde zumindest die folgenden Muster vorstellen:

      • Pipes-and-Filters: Zerlegt eine komplexe Aufgabe in eine Reihe von elementaren Aufgaben, die zusammengesetzt werden können.
      • Layers: Unterteilt das Softwaresystem in Schichten, wobei jede Schicht eine bestimmte Verantwortung hat und einen Dienst für eine höhere Schicht bereitstellt.
      • Model-View-Controller (MVC): Zerlegt eine (Benutzer-)Schnittstelle in die drei Komponenten Model, View und Controller:
        • Model: Der Kern der Anwendung, der die Views und Controls registriert; aktualisiert den View und den Controller.
        • View: Präsentiert die Informationen für den Benutzer; holt die Daten aus dem Modell.
        • Controller: Interagiert mit dem Benutzer und aktualisiert die Daten.
      • Reactor: Eine ereignisgesteuerte Anwendung, die mehrere Client-Anfragen gleichzeitig annehmen und an verschiedene Dienstanbieter weiterleiten kann.

Eine notwendige Voraussetzung für ein Data Race ist ein gemeinsamer, veränderbarer Zustand. Folgerichtig befassen sich die Synchronisationsmuster mit beiden Problemen. Hier sind die Synchronisationsmuster, über die ich schreiben möchte:

  • Copied value: Kopierte Daten können nicht Opfer eines Data Race werden.
  • Thread-specific storage: Ermöglicht einen globalen Zustand innerhalb eines Threads.
  • Futures: Nicht veränderbare Platzhalter für einen Wert, der durch einen Promise gesetzt wird.
  • Scoped locking: RAII angewandt auf Locks.
  • Strategized locking: Verschiedene Lockingstrategien einsetzen.
  • Thread-safe interface: Erweitert den kritischen Abschnitt auf ein Objekt.
  • Guarded suspension: Kombiniert einen Lock, der erworben werden muss, und eine Bedingung, die erfüllt sein muss, bevor eine Operation ausgeführt werden kann.

Außerdem müssen wir über eine concurrent Architektur nachdenken.

  • Active object: Trennt die Ausführung der Methode von ihrem Methodenaufruf.
  • Monitor object: Synchronisiert den Zugriff auf ein Objekt, sodass zu jedem Zeitpunkt nur eine Memberfunktion ausgeführt werden kann.

In meinem nächsten Artikel beginne ich meine Reise durch die "Design Patterns und Architekturmuster mit C++". Zuerst werde ich über die Ursprünge und die Geschichte der Entwurfsmuster schreiben. ()