We may earn an affiliate commission when you visit our partners.
Martin Schröder

Why you need design patterns

Read more

Why you need design patterns

  • Clean and maintainable: design patterns help us write clean, robust and maintainable code.

  • Enhanced code reuse: patterns help you decouple dependencies and keep your code organized.

  • Being proactive about bug prevention: design patterns give clear expectations and enable us to review and reason about large quantities of code more easily.

  • Removes ambiguity: using the same patterns consistently removes ambiguity about code structure and behavior.

  • Essential for effective DevOps: code that applies well known and understandable patterns is easy to review.

  • Make details fall into place: design patterns help organize the code and make small implementation details fall into place more easily.

When we develop embedded software, we see time and again the same class of problems. Legacy code that is costly to maintain, changing one piece of code results in unexpected behavioral changes in other places, code that breaks in unexpected ways.

All of this results in source code that becomes exponentially more difficult to work with.

What this all comes down to is lack of clear architectural guidelines for writing the software. Things like "How do we define an object?" or "What basic methods should every object have?". These things aren’t enforced by compilers and probably will never be. This is something that we must enforce with clear guidelines and code review.

This is why we have a list of guidelines that we apply to every project and we make sure that all developers know about these guidelines and point out to each other during code review when these guidelines are not being followed. This results in code that is clean, easy to maintain, easy to test and easy to understand too.

We use design patterns to clarify software structure and the expectations that we place on that structure.

Design patterns are essential for implementing efficient devops because they directly decrease the time it takes to review new code. When everyone on the team uses design patterns and agrees on their use, everyone also knows what to expect from new code when it follows already known patterns.

Design patterns help us write clean, robust and maintainable code.

Course Overview

We are going to start with a short introduction where I explain to you how to follow this course and where to find additional resources and how each module is structured.

We will then cover Creational Patterns which deal with construction of our data objects. Creational patterns help us have standardized ways of creating new objects and handling responsibility of memory ownership.

After that we dive into Structural Patterns which deal with the structure of our code. These patterns help us structure our code such that we can easily extend it with new functionality without having to refactor it later.

The Behavioral Patterns is a section concerned with code 'behavior' such as return value pattern. These patterns help us with certainty of what to expect from code in terms of behavior in different common situations.

Finally, the Concurrency Patterns will give you an intuitive understanding of concurrency on an embedded RTOS so that you can understand when to use which concurrency pattern in order to achieve high responsiveness of your application.

Introduction

  • Introduction. In this section we look at useful information that you need before getting started, where to find code examples for this training, how to get additional help and where you can ask questions regarding this course. This is a brief introduction to this course.

Creational Patterns

  • Object Pattern. This is a way to group data into objects that can be instantiated and destroyed. We also introduce concept of classes and member functions.

  • Opaque Pattern. This pattern gives us three ways of making the implementation of the object private and exposing only a handle to the object. This can also be referred to as opaque objects pattern.

  • Singleton Pattern. The singleton pattern is a design pattern used to ensure that a class has only one instance, while providing a global access point to this instance.

  • Factory Pattern. The factory design pattern is a creational design pattern that provides an interface for creating objects in a super class, but allows sub-classes to alter the type of objects that will be created.

Structural Patterns

  • Callback Pattern. Deals with object oriented callbacks that are bound to instances of objects. Allows callbacks to operate on specific instances of objects.

  • Inheritance Pattern. Inheritance pattern is used for implementing inheritance relationships between objects and components in a C program. It is useful as a way to create a hierarchy of objects instead of having to manage a large application where all details are at the same level of abstraction.

  • Virtual API Pattern. Virtual API pattern is a way of implementing virtual functions in C and making the handles to opaque objects also "smart". The virtual api pattern gives us polymorphism - where we can use the same interface for several implementations without knowing about these implementations.

  • Bridge Pattern. This pattern builds upon the virtual api pattern and is the pattern to use when you need to bridge two distinct hierarchies of objects. We cover an example in Rust and in C where we look at how this pattern can be used in practice.

Behavioral Patterns

  • Return Value Pattern. This pattern standardizes the way that function handle return values. This is valuable because return values in C are the primary way to signal status of an operation. Therefore we must have a clear way of communicating with caller through standardized return value.

Concurrency Patterns

  • Concurrency Introduction. In this section we are going to look at concurrency itself as a pattern for software development as well as when and why we should consider concurrency as a valuable tool in our toolbox.

  • Spinlock / Interrupt Masking Pattern. Masking interrupts is the simplest pattern for data integrity has to do with protecting shared data that is accessed by interrupts. A generic implementation of this is often done in the form of spinlock. Here we look at how we need to protect data from corruption by making sure interrupt never runs when we are modifying it.

  • Semaphore Pattern. A semaphore is one level above a spinlock and outlines a pattern of signalling between interrupt handlers and application level threads (and also between multiple threads in our application). In this module we look at the actual implementation of a semaphore, its use cases and important considerations. A semaphore is the most basic, thread aware synchronization primitive in any RTOS system.

  • Mutex Pattern. The mutex is slightly different from the semaphore in that it prevents starvation by means of priority inheritance. It is the primary pattern that should be used by an application thread for mutually exclusive access to a resource with respect to other threads. In this module we will look at an implementation of mutex, how it works and how it differs from implementations of other primitives.

  • Conditional Variable Pattern. The conditional variable pattern builds upon the mutex pattern to implement a way of signaling and waking up one or more threads from another thread or from an interrupt handler. This pattern is useful for checking for arbitrarily complex conditions in response to an asynchronous event in a safe manner.

Who this is for

This training is for:

  • Embedded Software Architects: even if you have been coding for many years, you may still find ideas in this content on how to do things better.

  • Beginner Embedded Programmers: patterns help you organize your code and the best place to start is right from the start.

  • Embedded Manager: it is very important that patterns are well understood and applied by the whole team because of how much impact they have on code structure.

Time to enroll.

Enroll now

What's inside

Learning objectives

  • Code readability and maintainability
  • Great code reuse
  • A clean software architecture
  • Easy code review

Syllabus

An overview of the whole course and introduction to the instructor
Introduction
Quiz: Introduction
To understand patterns associated with creation and lifecycle of objects in C
Read more
Object Pattern
Quiz: Object Pattern
Opaque Pattern
Quiz: Opaque Pattern
Singleton Pattern
Quiz: Singleton Pattern
Factory Pattern
Quiz: Factory Pattern
Ability to architecture a clean software structure
Callback Pattern
Quiz: Callback Pattern
Inheritance Pattern
Quiz: Inheritance Pattern
Virtual API Pattern
Quiz: Virtual API Pattern
Bridge Pattern
Quiz: Bridge Pattern
To standardize behavior of the code such as the return values from functions
Return Value Pattern
Quiz: Return Value Pattern
Ability to use concurrency to solve problems and understand what it does and how it works
Concurrency Pattern
Quiz: Concurrency Pattern
Spinlock Pattern
Quiz: Spinlock Pattern
Semaphore Pattern
Quiz: Semaphore Pattern
Mutex Pattern
Quiz: Mutex Pattern
Conditional Pattern
Quiz: Conditional Pattern

Good to know

Know what's good
, what to watch for
, and possible dealbreakers
Covers creational, structural, behavioral, and concurrency patterns, which provides a comprehensive overview of design patterns applicable to embedded systems development
Explores patterns like spinlock and mutex, which are essential for managing shared resources and preventing data corruption in multithreaded embedded environments
Teaches patterns that clarify software structure and expectations, which directly decreases the time it takes to review new code and implement efficient DevOps practices
Includes quizzes after each section, which allows learners to test their knowledge and reinforce their understanding of the material
Requires familiarity with C programming, which may necessitate additional learning for those without prior experience in this language
Focuses on design patterns, which may not be suitable for learners seeking a broader introduction to embedded systems or C programming fundamentals

Save this course

Save Embedded C Programming Design Patterns to your list so you can find it easily later:
Save

Activities

Be better prepared before your course. Deepen your understanding during and after it. Supplement your coursework and achieve mastery of the topics covered in Embedded C Programming Design Patterns with these activities:
Review C Fundamentals
Strengthen your understanding of C programming fundamentals, which are essential for grasping the more advanced design patterns used in embedded systems.
Show steps
  • Review basic C syntax and data types.
  • Practice pointer arithmetic and memory allocation.
  • Work through example C programs.
Review 'Design Patterns: Elements of Reusable Object-Oriented Software'
Gain a deeper understanding of design patterns by studying the classic text on the subject, which provides a solid foundation for applying these patterns in embedded C.
View Design Patterns on Amazon
Show steps
  • Read the introductory chapters on design principles.
  • Study the creational, structural, and behavioral patterns.
  • Relate the patterns to embedded C examples.
Implement a Simple RTOS Task Scheduler
Solidify your understanding of concurrency patterns by implementing a basic real-time operating system (RTOS) task scheduler, which will require you to apply concepts like mutexes, semaphores, and conditional variables.
Show steps
  • Design the task control block structure.
  • Implement task creation and deletion functions.
  • Create a simple scheduler loop.
  • Test with basic task switching.
Four other activities
Expand to see all activities and additional details
Show all seven activities
Document Code with Design Pattern Explanations
Improve code readability and maintainability by documenting your embedded C projects with clear explanations of the design patterns used, making it easier for others (and yourself) to understand and maintain the code.
Show steps
  • Choose a small embedded C project.
  • Identify the design patterns used.
  • Write detailed comments explaining each pattern.
  • Review the documentation for clarity.
Refactor Legacy Code with Design Patterns
Enhance your ability to apply design patterns by refactoring a piece of legacy embedded C code to improve its structure, readability, and maintainability, using the patterns learned in the course.
Show steps
  • Obtain a sample of legacy embedded C code.
  • Analyze the code for design flaws.
  • Apply appropriate design patterns to refactor the code.
  • Test the refactored code thoroughly.
Review 'Real-Time Concepts for Embedded Systems'
Deepen your understanding of real-time concepts and their application in embedded systems, which will help you make informed decisions when using concurrency patterns.
Show steps
  • Read chapters on task scheduling and synchronization.
  • Study the examples of real-time operating systems.
  • Relate the concepts to the course's concurrency patterns.
Contribute to an Open-Source Embedded Project
Apply your knowledge of embedded C and design patterns by contributing to an open-source embedded project, which will provide valuable real-world experience and expose you to different coding styles and project structures.
Show steps
  • Find an open-source embedded project on GitHub.
  • Identify a bug or feature to work on.
  • Implement the fix or feature using appropriate design patterns.
  • Submit a pull request with your changes.

Career center

Learners who complete Embedded C Programming Design Patterns will develop knowledge and skills that may be useful to these careers:
Embedded Software Engineer
An Embedded Software Engineer develops software for devices that are not general-purpose computers, such as microcontrollers in cars, medical devices, and industrial control systems. This course helps build a foundation for writing clean and maintainable code, which is critical for success in this role. A focus on design patterns empowers an embedded software engineer to create reusable and well-organized software. The course’s exploration of how to structure code and handle concurrency directly applies to the challenges faced when developing embedded systems. A future embedded software engineer will find the sections on object, opaque, singleton, and factory patterns particularly relevant for creating well-structured and robust software, and the sections on concurrency patterns invaluable for working with real-time embedded systems.
embedded systems architect
An Embedded Systems Architect designs the overall structure of embedded systems, making critical decisions about hardware and software integration. This course is designed for those who may want to become embedded system architects, and emphasizes the importance of design patterns in creating maintainable and efficient embedded software systems. The course helps develop the ability to structure software in a way that is easily understood and modified by teams of developers, which is crucial for large, complex projects. An embedded systems architect will find the breadth of patterns, such as the object, opaque, inheritance, virtual API, and bridge patterns, to be key in developing a system architecture. The concurrency patterns covered in the course are essential for any architect working with real-time embedded systems, providing the knowledge needed to make design choices that lead to reliable and performant software.
Firmware Engineer
A Firmware Engineer designs and develops low-level software that interacts directly with hardware. This course on design patterns is particularly helpful for a firmware engineer who wants to write code that is easy to maintain and extend. Firmware engineers must often work in resource-constrained environments, so understanding how to create clean, efficient, and reusable code, as taught in this course, is ideal. The course's emphasis on creational, structural, and behavioral patterns will allow a firmware engineer to organize code more effectively. The concurrency patterns are extremely useful for managing tasks and resources in real-time systems that firmware often controls. A firmware engineer will particularly benefit from the sections on callback, inheritance, and virtual API patterns.
Robotics Software Engineer
A Robotics Software Engineer develops software that controls and coordinates robotic systems. The course on design patterns helps a robotics software engineer write structured and robust code that can handle the complexities of robotic control. A robotics software engineer will benefit from the course's focus on code reuse, clean architecture, and maintainability. Robotic systems require efficient management of concurrent operations, making the concurrency patterns taught in this course highly beneficial. Furthermore, patterns like the factory, bridge, and inheritance patterns will allow a robotics software engineer to build modular and extensible systems. The course content on the return value pattern is helpful for writing robust robotics software that handles errors and status conditions effectively.
Automotive Software Engineer
An Automotive Software Engineer develops software for vehicles, including control systems, infotainment, and safety features. This course provides a strong foundation for an automotive software engineer, who needs to write code that is both reliable and maintainable. The course helps an automotive software engineer write code that can be easily reviewed and understood by other team members, aiding collaborative development. The emphasis on concurrency patterns will help automotive software engineers manage real-time and safety-critical vehicle systems. An automotive software engineer would benefit from the modules on the semaphore, mutex, and conditional variable patterns which are useful in safety critical embedded systems. The course helps an automotive software engineer build a strong foundation for designing automotive software.
Device Driver Developer
A Device Driver Developer writes low-level software that enables operating systems and applications to communicate with hardware devices. This course on design patterns helps a device driver developer write well-structured device drivers. The course also focuses on writing code that is easy to maintain and modify, which is essential in the device driver development field where code is often shared. A device driver developer can also use the concurrency patterns covered in this course to handle interaction between hardware and applications. The return value pattern is particularly helpful because drivers must handle status reporting and error conditions consistently and clearly. A future device driver developer can use the knowledge gained from the modules on the object, opaque, and singleton patterns in the development of modular and efficient device drivers.
Internet of Things Developer
An Internet of Things Developer creates software for smart devices that connect to the internet. This course may be useful for an Internet of Things developer who is interested in writing embedded software that is well-structured and efficient. This course focuses on software design patterns that help an Internet of Things developer write code that is clean and easy to maintain. The course's content on concurrency patterns will help manage the many real-time functions commonly found in IoT devices. An Internet of Things developer will find the modules on singleton, callback, and factory patterns especially useful in creating reliable and efficient software for IoT devices. The course’s emphasis on code reuse will also be helpful in the rapid development cycles typical in the IoT field.
Systems Programmer
A Systems Programmer develops low-level software that interacts with an operating system's kernel and hardware. The course emphasizes the development of maintainable and efficient code through design patterns, which is ideal for a systems programmer. The course will guide a systems programmer in creating code that is easy to maintain, modify, and integrate with other system components. The focus on concurrency patterns in this course will help a systems programmer handle the complex real-time aspects of working with computer systems. The modules on concurrency patterns, such as spinlock, semaphore, and mutex, are especially relevant for developing robust, concurrent, and thread-safe software. A systems programmer should take this course to understand how to structure complex systems properly.
Control Systems Engineer
A Control Systems Engineer designs and implements systems that control processes or equipment, often using embedded software. This course on design patterns may help a control systems engineer write code that is structured and robust. The course focuses on code clarity, which is crucial for teams working with complex control systems. The concurrency patterns covered in the course are especially relevant for control systems engineers who are working with real-time and safety-critical applications. The modules on the return value pattern are extremely useful when writing control systems code where it is essential to handle signal status. A control systems engineer may find the bridge and virtual API patterns particularly helpful in implementing flexible and adaptable control software.
Software Developer
A Software Developer writes code for a wide range of applications and systems and may find this course useful in developing good coding habits. The course emphasizes the significance of design patterns to write clean code that is easy to maintain. This ability is essential for any software developer. The course covers patterns that promote code reuse, which can be critical for software developers working on large or multiple projects. Although this course focuses on embedded programming, the principles of software architecture apply to all software development. A software developer will find the sections on creational, structural, and behavioral design patterns helpful in all kinds of programming scenarios. The course's emphasis on clear and consistent code can benefit developers regardless of their specific domain.
Technical Project Manager
A Technical Project Manager oversees and coordinates technical projects, ensuring that they are completed on time and within budget. This course may be useful for a technical project manager even though it does not directly support project management tasks. If a project manager is in charge of a project developing embedded software, having knowledge about software engineering will help them understand technical challenges. The project manager will need to understand technical constraints and how those impact timelines and resource allocation. A technical project manager will find the high-level concepts of software design patterns in this course may be helpful, but not essential to their work. A technical project manager may gain some insight into the process of software development which can help them estimate effort, and plan timelines more effectively. They may also see value in the topics that discuss team collaboration and code review.
Test Engineer
A Test Engineer is responsible for designing and implementing tests to ensure software quality. This course in embedded programming design patterns may be useful to a test engineer to understand how software works, so they can better develop and automate testing. The course focuses on code structure, which enables test engineers to better understand the architecture of the software under test. The course also discusses the use of design patterns to increase code clarity and reduce ambiguity which will be useful to test engineers in understanding the expected outcome of the software under test. A test engineer may find the design patterns covered to be useful, but not essential to their role. The more clearly coded and architected a piece of software is, the more easily test cases can be written. They will find the portions of the course covering code structure and clarity most relevant.
Systems Analyst
A Systems Analyst examines a business's systems to identify areas for improvement and designs solutions that may involve both software and hardware components. This course is not directly related to the core skills of a systems analyst, however, it may be useful to one who wishes to understand the embedded software development process. A systems analyst may find it helpful to understand the challenges of writing software in resource-constrained environments, because they might be called on to propose system-wide solutions in areas where embedded devices are involved. The course's concepts of software architecture, code clarity, and maintainability may be useful in a peripheral sense for a systems analyst when discussing solutions with software developer teams. While this course primarily focuses on engineering practices, it may provide additional context for a systems analyst looking to better understand the software systems that they work with.
Computer Science Professor
A Computer Science Professor teaches and conducts research in computer science. While this course focuses on practical software design patterns, a professor may want to learn more about the field of embedded systems to enhance a curriculum or pursue research in the area. A professor who teaches embedded programming may find the course's practical emphasis on design patterns to be useful in the development of teaching materials. A computer science professor who researches embedded systems will also find the practical nature of the course to be useful in understanding current best practice. A professor may additionally find the course's focus on concurrency useful for teaching advanced topics in operating systems and real-time systems.
Hardware Engineer
A Hardware Engineer designs and develops computer hardware components and systems. This course may be useful for a hardware engineer wanting to learn about how software engineers design their software, even though their role focuses on physical devices. The course may help a hardware engineer understand how software interacts with hardware at a low level. While the course focuses on software design patterns, the knowledge of how software is designed can help a hardware engineer make better hardware design decisions. An understanding of how software works with hardware will enable a hardware engineer to create a system where both elements can operate efficiently. A hardware engineer may find sections of the course on concurrency helpful in understanding the software side of hardware interactions.

Reading list

We've selected two books that we think will supplement your learning. Use these to develop background knowledge, enrich your coursework, and gain a deeper understanding of the topics covered in Embedded C Programming Design Patterns.
Is the seminal work on design patterns. It provides a comprehensive overview of various design patterns, their intent, motivation, applicability, structure, and implementation. While the examples are primarily in C++ and Smalltalk, the concepts are directly applicable to embedded C programming. This book is highly recommended as a reference to deepen your understanding of the patterns covered in the course.
Provides a comprehensive overview of real-time concepts relevant to embedded systems, including task scheduling, synchronization, and inter-process communication. It complements the course by providing a deeper understanding of the underlying principles behind concurrency patterns. It is especially useful for understanding the trade-offs involved in choosing different concurrency patterns for embedded applications. This book is valuable as additional reading to expand on the concurrency patterns covered in the course.

Share

Help others find this course page by sharing it with your friends and followers:

Similar courses

Similar courses are unavailable at this time. Please try again later.
Our mission

OpenCourser helps millions of learners each year. People visit us to learn workspace skills, ace their exams, and nurture their curiosity.

Our extensive catalog contains over 50,000 courses and twice as many books. Browse by search, by topic, or even by career interests. We'll match you to the right resources quickly.

Find this site helpful? Tell a friend about us.

Affiliate disclosure

We're supported by our community of learners. When you purchase or subscribe to courses and programs or purchase books, we may earn a commission from our partners.

Your purchases help us maintain our catalog and keep our servers humming without ads.

Thank you for supporting OpenCourser.

© 2016 - 2025 OpenCourser