Application of Strategy Pattern in C Language (Including 5 Examples + Linux Kernel Case Analysis)

Application of Strategy Pattern in C Language (Including 5 Examples + Linux Kernel Case Analysis)

Application of Strategy Pattern in C Language (Including Linux Kernel Examples)

1. Definition and Core Value of Strategy Pattern

The Strategy Pattern is a behavioral design pattern that focuses on defining a family of algorithms (or behaviors), encapsulating each algorithm as an independent strategy object, allowing algorithms to be dynamically replaced without affecting the client that uses them. This pattern achieves flexible extension and switching of algorithms by separating the definition and use of algorithms.

Significance: When a problem has multiple solutions (e.g., sorting algorithms like bubble sort, quick sort, merge sort, etc.), coupling all algorithms into client code can lead to bloated and hard-to-maintain client code, requiring modifications to client logic for new algorithms. The Strategy Pattern encapsulates algorithms as independent strategies, allowing the client to call algorithms through a unified interface, achieving “algorithm changes without client changes”.

Problems Solved:

  • • Strong coupling between multiple algorithms and client code leads to a large and hard-to-extend client;
  • • Adding or modifying algorithms requires changes to client logic, violating the “Open/Closed Principle”;
  • • Poor reusability of algorithms, requiring repeated implementations of the same algorithm in different scenarios.

2. Core Idea of Implementing Strategy Pattern in C Language

C language implements the pattern through structs encapsulating strategy interfaces + function pointers defining algorithm behaviors:

  1. 1. Define the strategy interface (Strategy): the struct contains function pointers corresponding to the core operations of the algorithm (e.g., <span>execute</span>);
  2. 2. Define the context class (Context): the struct holds a pointer to the strategy interface, providing an interface to set the strategy (<span>set_strategy</span>), and calls the algorithm through the strategy interface;
  3. 3. Implement specific strategies: implement function pointers of the strategy interface for each algorithm, allowing the client to dynamically replace the strategy pointer in the context class to switch algorithms.

Application of Strategy Pattern in C Language (Including 5 Examples + Linux Kernel Case Analysis)

3. 5 Examples

Example 1: Basic Strategy Pattern (Sorting Algorithm Switching)

Implement bubble sort and quick sort strategies, allowing the client to dynamically switch sorting algorithms.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 1. Strategy interface: Sorting algorithm
typedef struct {
    void (*sort)(struct SortStrategy* self, int* arr, int len);
} SortStrategy;

// 2. Context class: Sorter (using strategy)
typedef struct {
    SortStrategy* strategy; // Current sorting strategy
    void (*set_strategy)(struct Sorter* self, SortStrategy* s);
    void (*sort)(struct Sorter* self, int* arr, int len); // Delegate to strategy
} Sorter;

static void sorter_set_strategy(Sorter* self, SortStrategy* s) {
    self->strategy = s;
    printf("Sort strategy updated\n");
}

static void sorter_sort(Sorter* self, int* arr, int len) {
    if (self->strategy) {
        printf("Using current strategy to sort...\n");
        self->strategy->sort(self->strategy, arr, len);
    }
}

Sorter* create_sorter() {
    Sorter* sorter = malloc(sizeof(Sorter));
    sorter->strategy = NULL;
    sorter->set_strategy = sorter_set_strategy;
    sorter->sort = sorter_sort;
    return sorter;
}

// 3. Specific strategy 1: Bubble sort
static void bubble_sort(SortStrategy* self, int* arr, int len) {
    (void)self;
    printf("Executing bubble sort\n");
    for (int i = 0; i < len-1; i++) {
        for (int j = 0; j < len-i-1; j++) {
            if (arr[j] > arr[j+1]) {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
}

SortStrategy bubble_strategy = {.sort = bubble_sort};

// Specific strategy 2: Quick sort (simplified implementation)
static void quick_sort_recursive(int* arr, int left, int right) {
    if (left >= right) return;
    int pivot = arr[right];
    int i = left - 1;
    for (int j = left; j < right; j++) {
        if (arr[j] <= pivot) {
            i++;
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    int temp = arr[i+1];
    arr[i+1] = arr[right];
    arr[right] = temp;
    quick_sort_recursive(arr, left, i);
    quick_sort_recursive(arr, i+2, right);
}

static void quick_sort(SortStrategy* self, int* arr, int len) {
    (void)self;
    printf("Executing quick sort\n");
    quick_sort_recursive(arr, 0, len-1);
}

SortStrategy quick_strategy = {.sort = quick_sort};

// Helper function: Print array
void print_array(int* arr, int len) {
    for (int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

// Client usage
int main() {
    Sorter* sorter = create_sorter();
    int arr[] = {3, 1, 4, 1, 5, 9};
    int len = sizeof(arr)/sizeof(arr[0]);

    printf("Original array: ");
    print_array(arr, len);

    // Using bubble sort
    printf("\n--- Bubble sort ---\n");
    sorter->set_strategy(sorter, &bubble_strategy);
    sorter->sort(sorter, arr, len);
    print_array(arr, len);

    // Reinitialize array
    int arr2[] = {3, 1, 4, 1, 5, 9};
    printf("\nOriginal array: ");
    print_array(arr2, len);

    // Switch to quick sort
    printf("\n--- Quick sort ---\n");
    sorter->set_strategy(sorter, &quick_strategy);
    sorter->sort(sorter, arr2, len);
    print_array(arr2, len);

    free(sorter);
    return 0;
}

The output of the above code is as follows

Original array: 3 1 4 1 5 9 

--- Bubble sort ---
Sort strategy updated
Using current strategy to sort...
Executing bubble sort
1 1 3 4 5 9 

Original array: 3 1 4 1 5 9 

--- Quick sort ---
Sort strategy updated
Using current strategy to sort...
Executing quick sort
1 1 3 4 5 9 

The corresponding UML diagram is as follows

Application of Strategy Pattern in C Language (Including 5 Examples + Linux Kernel Case Analysis)

That is: the sorter (context class) calls the sorting algorithm through the strategy interface, and the client can dynamically switch between bubble sort and quick sort through <span>set_strategy</span>. Adding a new sorting algorithm (e.g., merge sort) only requires implementing a new strategy interface without modifying the sorter code.

Example 2: Compression Algorithm Strategy (File Compression Format Switching)

Simulate different compression algorithms (ZIP, GZIP) strategies, allowing the client to choose the compression format.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

// 1. Strategy interface: Compression algorithm
typedef struct {
    bool (*compress)(struct CompressStrategy* self, const char* data, char* out, int* out_len);
    const char* (*get_extension)(struct CompressStrategy* self); // Get file extension
} CompressStrategy;

// 2. Context class: Compressor
typedef struct {
    CompressStrategy* strategy;
    void (*set_strategy)(struct Compressor* self, CompressStrategy* s);
    bool (*compress)(struct Compressor* self, const char* data, char* out, int* out_len);
    const char* (*get_extension)(struct Compressor* self);
} Compressor;

static void compressor_set_strategy(Compressor* self, CompressStrategy* s) {
    self->strategy = s;
}

static bool compressor_compress(Compressor* self, const char* data, char* out, int* out_len) {
    if (self->strategy) {
        return self->strategy->compress(self->strategy, data, out, out_len);
    }
    return false;
}

static const char* compressor_get_extension(Compressor* self) {
    if (self->strategy) {
        return self->strategy->get_extension(self->strategy);
    }
    return "";
}

Compressor* create_compressor() {
    Compressor* comp = malloc(sizeof(Compressor));
    comp->strategy = NULL;
    comp->set_strategy = compressor_set_strategy;
    comp->compress = compressor_compress;
    comp->get_extension = compressor_get_extension;
    return comp;
}

// 3. Specific strategy 1: ZIP compression (simulation)
static bool zip_compress(CompressStrategy* self, const char* data, char* out, int* out_len) {
    (void)self;
    *out_len = snprintf(out, 1024, "[ZIP]%s[ZIP]", data);
    printf("ZIP compressed data: %s\n", out);
    return true;
}

static const char* zip_extension(CompressStrategy* self) {
    (void)self;
    return ".zip";
}

CompressStrategy zip_strategy = {
    .compress = zip_compress,
    .get_extension = zip_extension
};

// Specific strategy 2: GZIP compression (simulation)
static bool gzip_compress(CompressStrategy* self, const char* data, char* out, int* out_len) {
    (void)self;
    *out_len = snprintf(out, 1024, "[GZIP]%s[GZIP]", data);
    printf("GZIP compressed data: %s\n", out);
    return true;
}

static const char* gzip_extension(CompressStrategy* self) {
    (void)self;
    return ".gz";
}

CompressStrategy gzip_strategy = {
    .compress = gzip_compress,
    .get_extension = gzip_extension
};

// Client usage
int main() {
    Compressor* comp = create_compressor();
    const char* data = "Hello, Strategy Pattern!";
    char out[1024];
    int out_len;

    // Using ZIP compression
    printf("--- Using ZIP ---\n");
    comp->set_strategy(comp, &zip_strategy);
    comp->compress(comp, data, out, &out_len);
    printf("Output file: data%s\n", comp->get_extension(comp));

    // Switch to GZIP compression
    printf("\n--- Using GZIP ---\n");
    comp->set_strategy(comp, &gzip_strategy);
    comp->compress(comp, data, out, &out_len);
    printf("Output file: data%s\n", comp->get_extension(comp));

    free(comp);
    return 0;
}

The output of the above code is as follows

--- Using ZIP ---
ZIP compressed data: [ZIP]Hello, Strategy Pattern![ZIP]
Output file: data.zip

--- Using GZIP ---
GZIP compressed data: [GZIP]Hello, Strategy Pattern![GZIP]
Output file: data.gz

The corresponding UML diagram is as follows

Application of Strategy Pattern in C Language (Including 5 Examples + Linux Kernel Case Analysis)

That is: the compressor (context class) calls the compression algorithm through the strategy interface, and different strategies return the corresponding file extension. The client can dynamically choose the compression format based on needs, conforming to the design logic of file compression tools.

Example 3: Payment Strategy (Switching Between Multiple Payment Methods)

Simulate two strategies for Alipay and WeChat Pay, allowing the client to choose the payment method.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

// Payment information
typedef struct {
    const char* username;
    double amount;
} PaymentInfo;

// 1. Strategy interface: Payment method
typedef struct {
    bool (*pay)(struct PaymentStrategy* self, const PaymentInfo* info);
    const char* (*get_name)(struct PaymentStrategy* self); // Payment method name
} PaymentStrategy;

// 2. Context class: Payment Manager
typedef struct {
    PaymentStrategy* strategy;
    void (*set_strategy)(struct PaymentManager* self, PaymentStrategy* s);
    bool (*pay)(struct PaymentManager* self, const PaymentInfo* info);
} PaymentManager;

static void payment_set_strategy(PaymentManager* self, PaymentStrategy* s) {
    self->strategy = s;
}

static bool payment_pay(PaymentManager* self, const PaymentInfo* info) {
    if (self->strategy) {
        printf("Using %s to pay...\n", self->strategy->get_name(self->strategy));
        return self->strategy->pay(self->strategy, info);
    }
    return false;
}

PaymentManager* create_payment_manager() {
    PaymentManager* pm = malloc(sizeof(PaymentManager));
    pm->strategy = NULL;
    pm->set_strategy = payment_set_strategy;
    pm->pay = payment_pay;
    return pm;
}

// 3. Specific strategy 1: Alipay payment
static bool alipay_pay(PaymentStrategy* self, const PaymentInfo* info) {
    (void)self;
    printf("Alipay: User %s paid ¥%.2f\n", info->username, info->amount);
    return true;
}

static const char* alipay_name(PaymentStrategy* self) {
    (void)self;
    return "Alipay";
}

PaymentStrategy alipay_strategy = {
    .pay = alipay_pay,
    .get_name = alipay_name
};

// Specific strategy 2: WeChat Pay
static bool wechat_pay(PaymentStrategy* self, const PaymentInfo* info) {
    (void)self;
    printf("WeChat Pay: User %s paid ¥%.2f\n", info->username, info->amount);
    return true;
}

static const char* wechat_name(PaymentStrategy* self) {
    (void)self;
    return "WeChat Pay";
}

PaymentStrategy wechat_strategy = {
    .pay = wechat_pay,
    .get_name = wechat_name
};

// Client usage
int main() {
    PaymentManager* pm = create_payment_manager();
    PaymentInfo info = {.username = "user123", .amount = 99.99};

    // Using Alipay
    printf("--- Alipay ---\n");
    pm->set_strategy(pm, &alipay_strategy);
    pm->pay(pm, &info);

    // Switch to WeChat Pay
    printf("\n--- WeChat Pay ---\n");
    pm->set_strategy(pm, &wechat_strategy);
    pm->pay(pm, &info);

    free(pm);
    return 0;
}

The output of the above code is as follows

--- Alipay ---
Using Alipay to pay...
Alipay: User user123 paid ¥99.99

--- WeChat Pay ---
Using WeChat Pay to pay...
WeChat Pay: User user123 paid ¥99.99

The corresponding UML diagram is as follows

Application of Strategy Pattern in C Language (Including 5 Examples + Linux Kernel Case Analysis)

That is: the payment manager (context class) calls the payment logic through the strategy interface, with different payment methods encapsulated as independent strategies. The client can dynamically switch, and adding a new payment method (e.g., UnionPay) only requires implementing a new strategy without modifying the manager code.

Example 4: Logging Output Strategy (Console / File Log Switching)

Simulate logging output to console or file strategies, supporting dynamic switching of output targets.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

// 1. Strategy interface: Log output
typedef struct {
    void (*log)(struct LogStrategy* self, const char* level, const char* message);
} LogStrategy;

// 2. Context class: Logger
typedef struct {
    LogStrategy* strategy;
    void (*set_strategy)(struct Logger* self, LogStrategy* s);
    void (*info)(struct Logger* self, const char* message);
    void (*error)(struct Logger* self, const char* message);
} Logger;

static void logger_set_strategy(Logger* self, LogStrategy* s) {
    self->strategy = s;
}

static void logger_info(Logger* self, const char* message) {
    if (self->strategy) {
        self->strategy->log(self->strategy, "INFO", message);
    }
}

static void logger_error(Logger* self, const char* message) {
    if (self->strategy) {
        self->strategy->log(self->strategy, "ERROR", message);
    }
}

Logger* create_logger() {
    Logger* logger = malloc(sizeof(Logger));
    logger->strategy = NULL;
    logger->set_strategy = logger_set_strategy;
    logger->info = logger_info;
    logger->error = logger_error;
    return logger;
}

// Helper function: Get current time
static void get_current_time(char* buf, int len) {
    time_t t = time(NULL);
    struct tm* tm = localtime(&t);
    strftime(buf, len, "%Y-%m-%d %H:%M:%S", tm);
}

// 3. Specific strategy 1: Console log
static void console_log(LogStrategy* self, const char* level, const char* message) {
    (void)self;
    char time_buf[20];
    get_current_time(time_buf, sizeof(time_buf));
    printf("[%s] [%s] %s\n", time_buf, level, message);
}

LogStrategy console_strategy = {.log = console_log};

// Specific strategy 2: File log
static void file_log(LogStrategy* self, const char* level, const char* message) {
    (void)self;
    FILE* f = fopen("app.log", "a");
    if (!f) return;
    char time_buf[20];
    get_current_time(time_buf, sizeof(time_buf));
    fprintf(f, "[%s] [%s] %s\n", time_buf, level, message);
    fclose(f);
    printf("Log written to app.log\n");
}

LogStrategy file_strategy = {.log = file_log};

// Client usage
int main() {
    Logger* logger = create_logger();

    // Using console log
    printf("--- Console log ---\n");
    logger->set_strategy(logger, &console_strategy);
    logger->info(logger,"Program started");
    logger->error(logger,"Failed to read config");

    // Switch to file log
    printf("\n--- File log ---\n");
    logger->set_strategy(logger, &file_strategy);
    logger->info(logger,"User logged in");
    logger->error(logger,"Network timeout");

    free(logger);
    return 0;
}

The output of the above code is as follows

--- Console log ---
[2025-10-26 15:45:05] [INFO] Program started
[2025-10-26 15:45:05] [ERROR] Failed to read config

--- File log ---
Log written to app.log
Log written to app.log

The corresponding UML diagram is as follows

Application of Strategy Pattern in C Language (Including 5 Examples + Linux Kernel Case Analysis)

That is: the logger (context class) outputs logs through the strategy interface, with console and file logs encapsulated as independent strategies. The client can dynamically switch output targets, meeting the flexible configuration needs of logging systems.

Example 5: Kernel Style Strategy (Memory Allocation Algorithm)

Simulate the strategy pattern of Linux kernel memory allocation, supporting <span>kmalloc</span> (contiguous memory) and <span>vmalloc</span> (virtual memory) two allocation strategies.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

// 1. Strategy interface: Memory allocation algorithm
typedef struct {
    void* (*alloc)(struct AllocStrategy* self, size_t size);
    void (*free)(struct AllocStrategy* self, void* ptr);
    const char* (*get_name)(struct AllocStrategy* self);
} AllocStrategy;

// 2. Context class: Memory Manager (similar to kernel slab allocator)
typedef struct {
    AllocStrategy* strategy;
    void (*set_strategy)(struct MemManager* self, AllocStrategy* s);
    void* (*alloc)(struct MemManager* self, size_t size);
    void (*free)(struct MemManager* self, void* ptr);
} MemManager;

static void mem_set_strategy(MemManager* self, AllocStrategy* s) {
    self->strategy = s;
    printf("Memory allocator switched to %s\n", s->get_name(s));
}

static void* mem_alloc(MemManager* self, size_t size) {
    if (self->strategy) {
        return self->strategy->alloc(self->strategy, size);
    }
    return NULL;
}

static void mem_free(MemManager* self, void* ptr) {
    if (self->strategy && ptr) {
        self->strategy->free(self->strategy, ptr);
    }
}

MemManager* create_mem_manager() {
    MemManager* mm = malloc(sizeof(MemManager));
    mm->strategy = NULL;
    mm->set_strategy = mem_set_strategy;
    mm->alloc = mem_alloc;
    mm->free = mem_free;
    return mm;
}

// 3. Specific strategy 1: Contiguous memory allocation (simulate kmalloc)
static void* kmalloc_alloc(AllocStrategy* self, size_t size) {
    (void)self;
    void* ptr = malloc(size);
    printf("kmalloc: Allocated %zu bytes at %p (physical contiguous)\n", size, ptr);
    return ptr;
}

static void kmalloc_free(AllocStrategy* self, void* ptr) {
    (void)self;
    printf("kfree: Freed memory at %p\n", ptr);
    free(ptr);
}

static const char* kmalloc_name(AllocStrategy* self) {
    (void)self;
    return "kmalloc";
}

AllocStrategy kmalloc_strategy = {
    .alloc = kmalloc_alloc,
    .free = kmalloc_free,
    .get_name = kmalloc_name
};

// Specific strategy 2: Virtual memory allocation (simulate vmalloc)
static void* vmalloc_alloc(AllocStrategy* self, size_t size) {
    (void)self;
    // Simulate virtual address (non-contiguous physical memory)
    void* ptr = malloc(size);
    printf("vmalloc: Allocated %zu bytes at %p (virtual contiguous)\n", size, ptr);
    return ptr;
}

static void vmalloc_free(AllocStrategy* self, void* ptr) {
    (void)self;
    printf("vfree: Freed memory at %p\n", ptr);
    free(ptr);
}

static const char* vmalloc_name(AllocStrategy* self) {
    (void)self;
    return "vmalloc";
}

AllocStrategy vmalloc_strategy = {
    .alloc = vmalloc_alloc,
    .free = vmalloc_free,
    .get_name = vmalloc_name
};

// Client usage (simulate kernel module memory allocation)
int main() {
    MemManager* mm = create_mem_manager();

    // Using kmalloc allocation
    mm->set_strategy(mm, &kmalloc_strategy);
    void* ptr1 = mm->alloc(mm, 1024);
    void* ptr2 = mm->alloc(mm, 2048);

    // Switch to vmalloc allocation
    mm->set_strategy(mm, &vmalloc_strategy);
    void* ptr3 = mm->alloc(mm, 4096);

    // Free memory
    mm->free(mm, ptr1);
    mm->free(mm, ptr2);
    mm->free(mm, ptr3);

    free(mm);
    return 0;
}

The output of the above code is as follows

Memory allocator switched to kmalloc
kmalloc: Allocated 1024 bytes at 0x61e1e56696e0 (physical contiguous)
kmalloc: Allocated 2048 bytes at 0x61e1e5669af0 (physical contiguous)
Memory allocator switched to vmalloc
vmalloc: Allocated 4096 bytes at 0x61e1e566a300 (virtual contiguous)
vfree: Freed memory at 0x61e1e56696e0
vfree: Freed memory at 0x61e1e5669af0
vfree: Freed memory at 0x61e1e566a300

The corresponding UML diagram is as follows

Application of Strategy Pattern in C Language (Including 5 Examples + Linux Kernel Case Analysis)

That is: simulating the memory allocation strategies of the Linux kernel, <span>kmalloc</span> and <span>vmalloc</span> act as independent strategies, and the memory manager (context class) allocates/releases memory through the strategy interface, consistent with the design logic of memory allocators in the kernel (e.g., <span>slab</span>, <span>slub</span> allocators’ strategy switching).

4. Application of Strategy Pattern in Linux Kernel

  1. 1. Memory allocation strategies (<span>kmalloc</span>/<span>vmalloc</span>/<span>slab</span>): The kernel provides various memory allocation algorithms, <span>kmalloc</span><span> is used for allocating contiguous physical memory, </span><code><span>vmalloc</span><span> is used for allocating virtually contiguous memory, and </span><code><span>slab</span><span> is used for frequently allocating small objects. These algorithms are encapsulated through a unified </span><code><span>alloc_pages</span><span> interface, allowing kernel components to choose strategies based on needs.</span>
  2. 2. Scheduling strategies (<span>sched_class</span>): The kernel process scheduler supports multiple scheduling strategies (<span>CFS</span><span>/</span><code><span>RT</span><span>/</span><code><span>DL</span><span>), each strategy encapsulated in a </span><code><span>sched_class</span><span> structure (containing </span><code><span>enqueue_task</span><span>/</span><code><span>dequeue_task</span><span> function pointers), processes select scheduling strategies through </span><code><span>task_struct->policy</span><span>, and the scheduler calls the corresponding strategy's functions through a unified interface.</span>
  3. 3. File system operations (<span>file_operations</span><span>)</span>: Operations of different file systems (<span>ext4</span><span>/</span><code><span>btrfs</span><span>/</span><code><span>tmpfs</span><span>) are encapsulated in the </span><code><span>file_operations</span><span> structure as a strategy interface. VFS (Virtual File System) acts as the context class, calling the specific file system implementations through this structure, achieving "the same interface operates on different file systems".</span>
  4. 4. Network protocol strategies (<span>proto_ops</span><span>)</span>: Network protocols (<span>TCP</span><span>/</span><code><span>UDP</span><span>/</span><code><span>ICMP</span><span>) operations are encapsulated in the </span><code><span>proto_ops</span><span> structure, and the kernel calls specific protocol strategies through the </span><code><span>socket</span><span> interface, allowing applications to focus on protocol types (e.g., </span><code><span>SOCK_STREAM</span><span> corresponds to TCP).</span>
  5. 5. Encryption algorithm strategies (<span>crypto_alg</span><span>)</span>: The kernel encryption framework supports multiple encryption algorithms (<span>AES</span><span>/</span><code><span>SHA</span><span>/</span><code><span>DES</span><span>), each algorithm encapsulated in the </span><code><span>crypto_alg</span><span> structure (containing </span><code><span>cra_init</span><span>/</span><code><span>cra_exit</span><span>/</span><code><span>cra_cipher</span><span> operations), users select algorithms through </span><code><span>crypto_alloc_alg</span><span>, and the framework calls the corresponding strategies through a unified interface.</span>

5. Implementation Considerations

  1. 1. Consistency of strategy interfaces: All specific strategies must implement all function pointers of the strategy interface to avoid crashes due to unimplemented interfaces (can implement empty operations or error logic in default strategies).
  2. 2. Statelessness of strategies: Strategy objects should be designed to be stateless (i.e., not relying on internal member variables), allowing strategies to be shared (e.g., global static strategy instances), reducing memory overhead. If a strategy requires state, it should be passed through the context class or dynamically created.
  3. 3. Timing of strategy selection: Strategies can be selected at compile time (via macro definitions) or runtime (via <span>set_strategy</span><span>). Runtime switching is suitable for scenarios that need to adapt dynamically to the environment (e.g., selecting sorting algorithms based on data volume), while compile-time selection is suitable for fixed scenarios to reduce runtime overhead.</span>
  4. 4. Avoid strategy bloat: If there are too many strategies (e.g., dozens of algorithms), consider combining with the factory pattern to create strategies, dynamically obtaining strategy instances by name or ID, avoiding direct client dependencies on all strategies.
  5. 5. Thread safety: In a multithreaded environment, if strategies are shared instances and stateless, they are naturally thread-safe; if strategies are stateful or the context class dynamically switches strategies, mutexes should protect access and switching of strategies.

6. Additional Notes

  • • Difference between Strategy Pattern and State Pattern: The Strategy Pattern emphasizes active selection and replacement of algorithms (the client decides when to switch strategies), while the State Pattern emphasizes behavior changes driven by state (the state internally decides when to switch states), the former is “external selection”, the latter is “internal driving”.
  • • Extensions of the Strategy Pattern: Can enhance functionality through strategy chains (multiple strategies executed in combination) or strategy decorators (adding extra logic before and after strategies), such as logging strategies combining “console output + file output”.
  • • Applicable scenarios: Scenarios with multiple selectable algorithms (sorting, compression, encryption), scenarios where behavior needs to be dynamically adjusted (payment methods, log output), and scenarios with high algorithm reuse rates (kernel components, general libraries).

Through the Strategy Pattern, C language programs (especially in the Linux kernel) can achieve flexible extension and switching of algorithms, significantly reducing the coupling between algorithms and clients, making systems easier to maintain and evolve. Many core components in the kernel adopt the Strategy Pattern, which is a core technology for building highly reusable and configurable systems.

Click to read the full article, thank you for sharing, saving, liking, and viewing

Leave a Comment