When to Replace if‑else with Strategy Pattern in Embedded Systems
This article compares traditional if‑else/switch‑case branching with the Strategy design pattern in embedded development, illustrating their differences through analogies and code examples, and explains when to adopt Strategy for better extensibility, lower coupling, and maintainability while noting scenarios where simple branching remains preferable.
Preface
In embedded development, if‑else (or switch‑case) is widely used, but as conditions increase the code becomes highly coupled, forming a "spaghetti" structure. The problem is not the branch itself but its procedural nature leading to tight coupling.
Conclusion: Branching and Strategy pattern are not about superiority; they serve different needs. Strategy focuses on “who does it”, decoupling “what to do” from “how to do it”, allowing each branch to use the same interface and only modify strategy modules.
Illustration
Analogy: if‑else is like a one‑piece screwdriver; changing the screw head requires a whole new tool. Strategy is like an electric screwdriver with interchangeable bits.
if‑else mode: integrated screwdriver
To change a screw head you must replace the whole screwdriver; if you don’t have the required type you cannot work.
if (screw_type == PHILLIPS) {
use_phillips_screwdriver();
}
else if (screw_type == SLOT) {
use_slot_screwdriver(); // if this else if is missing, it fails
}Strategy mode: electric screwdriver
The main unit is stable; each bit represents a different strategy.
The main program only holds the driver and issues a “turn” command.
The actual screw type depends on which bit is attached.
Adding a new screw type only requires a new bit, not a new driver.
Example
if‑else limitation
Example with motor control using multiple if‑else branches.
typedef enum {
MOTOR_TYPE_DC,
MOTOR_TYPE_STEPPER,
MOTOR_TYPE_SERVO
} motor_type_t;
void motor_control(motor_type_t type, void *args) {
if (type == MOTOR_TYPE_DC) {
uint8_t percent = *((uint8_t*)args);
dc_motor_set_speed(percent);
} else if (type == MOTOR_TYPE_STEPPER) {
int32_t steps = *((int32_t*)args);
stepper_motor_step(steps);
} else if (type == MOTOR_TYPE_SERVO) {
uint8_t degree = *((uint8_t*)args);
servo_motor_set_angle(degree);
}
}Problems: rigid extension (violates Open/Closed), high coupling, poor maintainability.
Strategy solution
Define a strategy interface and implement each motor type as a separate strategy.
// motor_strategy.h
typedef void (*control_func_t)(struct MotorController* controller, void* args);
typedef struct MotorController {
control_func_t control;
void* device_data;
} motor_controller_t;Implement concrete strategies, e.g., DC motor.
// dc_motor.c
typedef struct {
TIM_HandleTypeDef* pwm_tim;
uint32_t pwm_channel;
} dc_motor_data_t;
static void dc_motor_control(motor_controller_t* controller, void* args) {
uint8_t percent = *((uint8_t*)args);
dc_motor_data_t* data = (dc_motor_data_t*)(controller->device_data);
uint32_t ccr = (percent * (data->pwm_tim->Init.Period)) / 100;
__HAL_TIM_SET_COMPARE(data->pwm_tim, data->pwm_channel, ccr);
}
void dc_motor_init(motor_controller_t* controller, TIM_HandleTypeDef* pwm_tim, uint32_t pwm_channel) {
dc_motor_data_t* data = malloc(sizeof(dc_motor_data_t));
data->pwm_tim = pwm_tim;
data->pwm_channel = pwm_channel;
controller->control = dc_motor_control;
controller->device_data = data;
}Use the strategy:
motor_controller_t dc_motor;
motor_controller_t stepper_motor;
int main() {
// Initialize strategies
dc_motor_init(&dc_motor, &htim1, TIM_CHANNEL_1);
stepper_motor_init(&stepper_motor, GPIOA, GPIO_PIN_5, GPIOA, GPIO_PIN_6);
uint8_t dc_speed = 50;
int32_t stepper_steps = 100;
while (1) {
motor_control(&dc_motor, &dc_speed); // DC motor strategy
HAL_Delay(1000);
motor_control(&stepper_motor, &stepper_steps); // Stepper motor strategy
HAL_Delay(1000);
}
}Advantages of Strategy
Open/Closed Principle : Adding new motor types only requires a new strategy module.
Low Coupling : Each motor logic is encapsulated and can be tested independently.
Extensibility : New features do not affect existing code.
Clear Code : Single responsibility per module improves readability and maintenance.
Summary
When to use Strategy
Scenario
Recommended
Reason
System needs to switch among multiple algorithms or strategies
Strategy pattern
Better extensibility and maintainability
Complex logic with many conditions needs clear separation
Strategy pattern
Reduces coupling, improves readability
Algorithms may change independently or require independent testing
Strategy pattern
Facilitates unit testing and modular development
Simple, stable logic that rarely changes
if‑else
Direct, minimal overhead
Performance‑critical scenarios
if‑else
Avoids function‑pointer overhead
Core differences
Aspect
if‑else / switch‑case
Strategy pattern
Design philosophy
Procedural: focus on “how to do”
Object‑oriented: focus on “who does it”, decoupling “what” from “how”
Code structure
Vertical growth: more branches inside a function
Horizontal growth: add new strategy classes without touching existing code
Open/Closed
Violated: must modify existing code
Observed: add new modules only
Coupling
High
Low
Readability
Poor with many branches
Good, each strategy is single‑purpose
Maintainability
Low
High
Performance overhead
None
One function‑pointer call (negligible in most embedded apps)
Memory usage
Low
Slightly higher (strategy objects)
Suitable scenarios
Simple, stable logic
Frequent extensions, multiple algorithms
Some Advice
Don’t overuse Strategy
If there is only one motor type and no future extensions, a simple function call is best. Follow the KISS principle.
Gradual adoption
When you see repeated if (device_type == A) checks and need to add new device types, consider Strategy.
Recommended use cases
System needs to switch among multiple algorithms or strategies
Complex logic with many conditions requires clear separation
Algorithms may change independently or need independent testing
Simple scenarios still suit if‑else
Logic is simple and stable
Performance‑critical situations where even a tiny overhead matters
Remember: choosing the right design pattern is more important than using a complex one for its own sake.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
