Embedded Development · Design Patterns
Die 10 wichtigsten Design Patterns fuer Embedded-Entwicklung
Design Patterns in Embedded sind keine akademische Uebung. Sie sind der Unterschied zwischen Software, die 10 Jahre in der Produktion laeuft, und Software, die nach dem ersten Feldupdate zusammenbricht.
Gang-of-Four-Buecher stehen in jedem Regal. In der Embedded-Praxis werden trotzdem die gleichen Fehler wiederholt: globale Zustaende, Spaghetti-Interrupts, monolithische main-Schleifen. Die folgenden 10 Patterns sind keine Theorie — sie laufen in Geraeten, die seit Jahren im Feld stehen.
1. State Machine (Zustandsautomat)
Problem: Komplexe Geraetezustaende (Init, Ready, Active, Error, Shutdown) mit unklaren Uebergaengen. Wann: Jedes Embedded-Geraet mit Modi. Keine Ausnahmen.
enum class State { Init, Ready, Active, Error, Shutdown };
void transition(State& current, Event event) {
static const std::map<std::pair<State,Event>, State> table = {
{{State::Init, Event::Configured}, State::Ready},
{{State::Ready, Event::Start}, State::Active},
{{State::Active, Event::Fault}, State::Error},
};
auto it = table.find({current, event});
if (it != table.end()) current = it->second;
}
2. Observer (Signal/Slot)
Problem: Sensor A aendert sich — UI, Logger und Controller muessen reagieren. Wann: Jedes Multi-Komponenten-System.
class SensorPublisher {
std::vector<std::function<void(float)>> subscribers_;
public:
void subscribe(std::function<void(float)> cb) { subscribers_.push_back(cb); }
void notify(float value) { for (auto& cb : subscribers_) cb(value); }
};
3. Command Pattern
Problem: Undo/Redo, Remote-Steuerung, gereihte Operationen. Wann: CNC-Maschinen, Medizingeraete-Prozeduren, Testsequenzen.
struct Command {
virtual void execute() = 0;
virtual void undo() = 0;
virtual ~Command() = default;
};
std::stack<std::unique_ptr<Command>> history;
4. Singleton (mit Vorsicht)
Problem: Hardware-Abstraktionsschicht braucht genau eine Instanz. Wann: HAL, Konfigurationsmanager, Logger. Nicht fuer alles andere. Bevorzugen Sie Dependency Injection.
class HardwareManager {
public:
static HardwareManager& instance() {
static HardwareManager inst; // Meyers Singleton
return inst;
}
private:
HardwareManager() = default;
};
5. Producer-Consumer (Ring Buffer)
Problem: Sensordaten kommen schneller als die Verarbeitung. Wann: ADC-Sampling, UART-Empfang, jede ISR-zu-Task-Kommunikation.
template<typename T, size_t N>
class RingBuffer {
std::array<T, N> buf_;
std::atomic<size_t> head_{0}, tail_{0};
public:
bool push(const T& val) { /* lock-free write */ }
bool pop(T& val) { /* lock-free read */ }
};
6. Strategy Pattern
Problem: Gleicher Algorithmus, verschiedene Implementierungen (Kalman vs. Moving Average vs. Median-Filter). Wann: Sensorfusion, Kommunikationsprotokolle, Verschluesselungsmodi.
struct FilterStrategy {
virtual float apply(std::span<const float> data) = 0;
};
class KalmanFilter : public FilterStrategy { /* ... */ };
class MedianFilter : public FilterStrategy { /* ... */ };
7. Factory Pattern
Problem: Objekterzeugung basierend auf Laufzeitkonfiguration (welcher Sensor? welches Protokoll?). Wann: Plugin-Systeme, Multi-Protokoll-Geraete, konfigurierbare Test-Setups.
std::unique_ptr<Sensor> createSensor(const Config& cfg) {
if (cfg.type == "gnss") return std::make_unique<GnssSensor>(cfg);
if (cfg.type == "imu") return std::make_unique<ImuSensor>(cfg);
throw std::runtime_error("Unknown sensor: " + cfg.type);
}
8. RAII
Problem: Vergessene Freigabe von Hardware-Ressourcen (GPIO, SPI-Bus, File Handles). Wann: Immer in C++. Das ist nicht verhandelbar.
class SpiGuard {
SPI_HandleTypeDef* handle_;
public:
SpiGuard(SPI_HandleTypeDef* h) : handle_(h) { HAL_SPI_Init(handle_); }
~SpiGuard() { HAL_SPI_DeInit(handle_); }
SpiGuard(const SpiGuard&) = delete;
};
9. Facade Pattern
Problem: Komplexes Subsystem (Netzwerk-Stack, Display-Treiber) mit zu vielen Einstiegspunkten. Wann: Wrapper fuer Vendor-SDKs, Hardware-Abstraktion, Third-Party-Library-Integration.
class DisplayFacade {
public:
void showStatus(const std::string& msg); // intern: 12 Vendor-API-Calls
void showError(int code); // intern: Farbe, Icon, Buzzer
void clear();
};
10. Watchdog Pattern
Problem: System haengt, undefinierter Zustand. Wann: Jedes Embedded-System in Produktion. Wenn Sie keinen Watchdog haben, haben Sie kein Produkt.
class SoftwareWatchdog {
std::chrono::steady_clock::time_point last_kick_;
std::chrono::milliseconds timeout_;
public:
void kick() { last_kick_ = std::chrono::steady_clock::now(); }
bool expired() const {
return (std::chrono::steady_clock::now() - last_kick_) > timeout_;
}
};
Beispiel aus der Praxis
In einem Vermessungssystem (GNSS-basiert, Qt/C++) verwenden wir 7 der 10 Patterns gleichzeitig: State Machine fuer Geraetezustaende, Observer fuer Sensordaten, Ring Buffer fuer die GNSS-Rohdaten, RAII fuer die serielle Schnittstelle, Factory fuer die Protokollwahl, Facade fuer den Vendor-SDK-Wrapper, Watchdog fuer die Feldueberwachung. Kein Pattern ist Selbstzweck — jedes loest ein konkretes Problem, das wir im Feld erlebt haben.
Landsberg am Lech · alpitype.de
Weiterführende Artikel
KI nutzen, ohne Daten in die Cloud zu schicken
On-premise KI: Wie Systeme vollständig lokal betrieben werden.
Sind Ihre Daten überhaupt für KI nutzbar?
Datenqualität prüfen, bevor Sie in KI investieren.
Was KI in einem realen Industrieprojekt kostet
Konkrete Zahlen, Phasen und ROI aus realen Projekten.
Nicht sicher, ob das auf Ihren Fall zutrifft?
Wir prüfen Ihr Setup in 2 Wochen und sagen Ihnen, ob KI machbar ist.
Machbarkeits-Audit anfragen →Sprechen Sie mit einem Ingenieur
Kein Vertrieb. Sie sprechen direkt mit einem unserer Software-Architekten über Ihr konkretes Problem. 30 Minuten. Antwort innerhalb von 24 Stunden.
Email: info@alpitype.com
LinkedIn: AlpiType