C Language – Design Patterns and Object-Oriented Design (C3)

Programming, C, C++

C is often seen as a low-level tool for high-performance code, but it can produce well-structured, modular and maintainable systems. Using header files, opaque types, function interfaces and linker features, C supports clear component boundaries.

This course shows how to design software in plain C using common design patterns, idioms and architectural principles used in real projects. You will learn to build component-based systems, apply OOP concepts with minimal overhead while keeping code testable and maintainable.

THIS TRAINING COURSE WILL HELP YOU:

  • Build object-like data models using structs with function pointers
  • Implement and test common design patterns in plain C
  • Choose when a pattern is appropriate in real projects
  • Avoid pattern overuse and reduce overengineering

WHO SHOULD ATTEND?

  • Developers working in C on production systems
  • Embedded and low-level systems programmers
  • Library, middleware and driver authors
  • Developers aiming for modular, testable C code

COURSE LOCATION AND AVAILABLE DATES



This course is not scheduled as a public course.
It is delivered exclusively as customised training. The content, duration, date, and delivery format are tailored to the customer's requirements and can also be provided in English.

Request customised training

Course content:

Hide details
  • Clear, maintainable code written for humans
    1. Use established practices and design patterns
    2. Describe software as components and behaviors
    3. Separate behavior from implementation details
  • Object-oriented software design
    1. Program composed of cooperating components
    2. Components have assigned responsibilities
    3. Think about each component’s role and purpose
  • Introducing object-oriented programming
    1. Virtual methods via function pointers
    2. Abstract interfaces using operations structs
    3. Object-oriented architecture
  • Differences from object-oriented languages
    1. Large amounts of boilerplate code
    2. Casting without strict type checks
    3. Imitating inheritance with base functions
  • Alternatives for implementing OOP
    1. Function pointers stored directly with data
    2. Rust-like architectures and fat pointers
    3. Static polymorphism via linking
  • Dependencies between components
    1. Pointers and ownership
    2. Responsibility for object destruction
    3. Removing memory leaks
  • Principles of good software design
    1. Component independence and localized changes (SRP)
    2. Depend on interfaces, not implementations (DIP)
    3. Keep code closed to modification when extending (OCP)
  • Changing component behavior at runtime
    1. State machine and the State pattern
    2. Implementation with switch and enums
    3. Differences from a pure state machine
  • Bridging interfaces using structural patterns
    1. Separate different domains (Bridge)
    2. Bridge mismatched interfaces (Adapter)
    3. Expose a simplified interface (Facade)
  • Chaining via a single interface
    1. Add behavior to existing systems (Decorator)
    2. Chain possible handlers (Chain of Responsibility)
    3. Control and mediate access to objects (Proxy)
  • Linear data structures and sequences
    1. Traversing structures (Iterator)
    2. Distinguish linked list and chain structures
    3. Terminate without null pointers (Null Object)
  • Composing complex objects and components
    1. Branch by concrete type (Visitor)
    2. Tree structure of objects (Composite)
    3. Solutions using union and switch combinations
  • Creating diverse object types
    1. Factory for all kinds of objects (Factory)
    2. Abstract factory for object families (Abstract Factory)
    3. Use function pointer for type registration
  • Objects with a single instance
    1. Create a single instance on first use (Singleton)
    2. Manage global resources
    3. Stateful communication with the OS
  • Multi-step object construction
    1. Builder object as an intermediate step (Builder)
    2. Methods to modify the builder and finalize build
    3. Split the builder into multiple phases
  • Signaling and event handling
    1. Central coordinating component (Mediator)
    2. Register event subscribers (Observer)
    3. Signals with function pointer callbacks (signals & slots)
  • Generation, streaming and lazy evaluation
    1. Process sequences and pipelining (iterators)
    2. Use message queues
    3. Generate state machines without callbacks
  • Representing data changes
    1. Describe a concrete change (transaction, command)
    2. Sequence of subsequent changes (journal)
    3. Use mini-language or bytecode (interpreter)
  • Persisting and restoring state
    1. Create snapshots of state data (snapshot, memento)
    2. Handle history of snapshots (history, recovery)
    3. Combine snapshots and transactions (incremental backup)
  • Optimizing data handling
    1. Data structures with instant snapshots (copy-on-write)
    2. Share mostly immutable parameters (flyweight)
    3. Clone objects and the "rule of zero" (clone, prototype)
Prerequisites:
Good knowledge of C programming.
Recommended previous course:
C Language — Practical Advanced Programming (C2)
Schedule:
4 days (9:00-17:00)

Training and learning environment