說明
嵌入式系統(tǒng)常常需要對不同的輸入采取不同的行為,例如按下按鈕后的操作、傳感器讀數(shù)后的處理、接收到的通信數(shù)據(jù)的解析等等。在這種情況下,策略模式可以提供一種清晰、可擴展的解決方案。本文將介紹嵌入式C語言中策略模式的基本原理和實現(xiàn)方法。
策略模式的實現(xiàn)方式通常包括以下三個部分:
定義策略接口:定義一個抽象的接口或基類,該接口或基類包含了不同策略的通用方法。
實現(xiàn)具體策略:實現(xiàn)不同的策略類,每個策略類都實現(xiàn)了策略接口或基類中定義的方法。
選擇策略:在運行時根據(jù)需要選擇特定的策略對象,并調(diào)用其方法。
下面給出幾個嵌入式C語言中策略模式的案例:
- 控制器:一個嵌入式控制器需要根據(jù)不同的傳感器數(shù)據(jù)選擇不同的控制策略。例如,在溫度傳感器的值高于某個閾值時,選擇降溫策略,否則選擇加熱策略。
- 數(shù)據(jù)通信:在嵌入式設(shè)備之間的通信中,可能需要選擇不同的通信協(xié)議或傳輸方式。例如,在通過RS-232串口進行通信時,需要選擇串口通信策略;在通過網(wǎng)絡(luò)進行通信時,需要選擇網(wǎng)絡(luò)通信策略
- 顯示控制:一個嵌入式顯示控制器需要根據(jù)不同的顯示需求選擇不同的顯示策略。例如,在顯示數(shù)字時,需要選擇數(shù)字顯示策略;在顯示文本時,需要選擇文本顯示策略。
- 定時器:在嵌入式系統(tǒng)中,可能需要定期執(zhí)行不同的任務(wù)。例如,在一個定時器中,可以注冊不同的定時任務(wù),每個任務(wù)實現(xiàn)不同的功能。
- 外設(shè)控制:在嵌入式系統(tǒng)中,需要對不同的外設(shè)進行控制。例如,在驅(qū)動一個電機時,需要選擇不同的控制方式,如PWM、脈沖計數(shù)等。
在這些案例中,策略模式能夠提高系統(tǒng)的靈活性和可維護性,使系統(tǒng)能夠在運行時根據(jù)不同的情況選擇不同的算法或行為,同時降低代碼的復(fù)雜度和耦合度。
上面只是舉例的一些,實際在很多場景中用到,即使點亮1個LED燈也可以用得到。
1- 策略模式概述
策略模式是一種對象行為型模式,它定義了一系列的算法,并將每一個算法封裝起來,使它們可以互相替換。策略模式讓算法的變化獨立于使用算法的客戶端,從而實現(xiàn)了算法的動態(tài)切換。
在嵌入式系統(tǒng)中,策略模式的主要目的是將不同的行為或動作封裝在不同的算法對象中,并使得這些算法對象可以在運行時根據(jù)需要動態(tài)切換。這種設(shè)計模式可以讓嵌入式系統(tǒng)更加靈活,易于維護和擴展。
2- 嵌入式C語言中的策略模式
嵌入式C語言中的策略模式通常采用函數(shù)指針來實現(xiàn)。每個策略被封裝在一個函數(shù)中,并通過函數(shù)指針的形式存儲在一個結(jié)構(gòu)體中??蛻舳舜a可以通過修改結(jié)構(gòu)體中的函數(shù)指針來切換算法。下面是一個簡單的例子:
typedef struct {
void (*foo)(void);
void (*bar)(void);
} Strategy;
void foo1(void) {
// do something
}
void foo2(void) {
// do something else
}
void bar1(void) {
// do another thing
}
void bar2(void) {
// do yet another thing
}
void main(void) {
Strategy strategy = {foo1, bar1};
// ...
strategy.foo();
strategy.bar();
// ...
strategy.foo = foo2;
strategy.bar = bar2;
// ...
strategy.foo();
strategy.bar();
// ...
}
在這個例子中,Strategy 結(jié)構(gòu)體包含了兩個函數(shù)指針 foo 和 bar,分別表示兩種不同的算法。在程序運行時,可以通過修改 Strategy 結(jié)構(gòu)體中的函數(shù)指針來切換算法。
當(dāng)然,上面的例子還比較簡單,實際中的策略模式往往需要更加復(fù)雜的數(shù)據(jù)結(jié)構(gòu)和算法。下面我們來看一些更加實際的例子。
在嵌入式系統(tǒng)中,常常需要對按鍵事件進行處理,例如按下按鈕后要執(zhí)行某種操作。不同的按鍵事件需要執(zhí)行不同的操作,因此可以將按鍵事件的處理封裝成一個策略模式。
下面是一個例子,我們假設(shè)系統(tǒng)中有兩個按鍵,分別為 BUTTON1 和 BUTTON2,按下不同的按鍵需要執(zhí)行不同的操作。
首先定義一個策略接口:
typedef void (*ButtonStrategy)(void);
然后定義不同的算法,即按鍵事件的處理函數(shù):
void Button1Strategy(void) {
// do something when button 1 is pressed
}
void Button2Strategy(void) {
// do something when button 2 is pressed
}
接下來定義一個策略表,其中每個元素是一個策略對象:
typedef struct {
uint8_t button; // 按鈕編號
ButtonStrategy strategy; // 策略函數(shù)指針
} ButtonStrategyTable;
static const ButtonStrategyTable buttonStrategies[] = {
{1, Button1Strategy},
{2, Button2Strategy},
};
最后,在中斷處理函數(shù)中根據(jù)按鍵編號查找策略表,并執(zhí)行相應(yīng)的策略:
void ButtonInterruptHandler(uint8_t button) {
for (size_t i = 0; i < sizeof(buttonStrategies) / sizeof(buttonStrategies[0]); i++) {
if (buttonStrategies[i].button == button) {
buttonStrategies[i].strategy();
break;
}
}
}
在這個例子中,我們使用一個策略表來存儲不同的策略對象,每個對象包含一個按鈕編號和一個策略函數(shù)指針。當(dāng)某個按鈕被按下時,中斷處理函數(shù)會根據(jù)按鈕編號查找策略表,找到相應(yīng)的策略并執(zhí)行。
3-傳感器數(shù)據(jù)處理
另一個常見的嵌入式系統(tǒng)應(yīng)用場景是傳感器數(shù)據(jù)處理。例如,系統(tǒng)中可能需要讀取多個傳感器的數(shù)據(jù),并根據(jù)數(shù)據(jù)的不同進行不同的處理。
我們可以將每個傳感器的數(shù)據(jù)處理封裝成一個策略對象,然后使用一個策略表來管理這些對象。
下面是一個例子,我們假設(shè)系統(tǒng)中有兩個傳感器,分別為 SENSOR1 和 SENSOR2,并且它們的數(shù)據(jù)類型不同,分別為 uint16_t 和 float。對于不同的數(shù)據(jù)類型,我們需要執(zhí)行不同的處理操作。
首先定義一個策略接口
typedef void (*SensorStrategy)(void* data);
然后定義不同的算法,即傳感器數(shù)據(jù)的處理函數(shù):
void Sensor1Strategy(void* data) {
uint16_t sensorData = *(uint16_t*)data;
// do something with sensorData
}
void Sensor2Strategy(void* data) {
float sensorData = *(float*)data;
// do something with sensorData
}
接下來定義一個策略表,其中每個元素是一個策略對象:
typedef struct {
uint8_t sensor; // 傳感器編號
size_t dataSize; // 數(shù)據(jù)大小
SensorStrategy strategy; //
} SensorStrategyTable;
static const SensorStrategyTable sensorStrategies[] =
{{1, sizeof(uint16_t), Sensor1Strategy},
{2, sizeof(float), Sensor2Strategy},};
最后,在讀取傳感器數(shù)據(jù)時,根據(jù)傳感器編號查找策略表,并執(zhí)行相應(yīng)的策略:
void ReadSensorData(uint8_t sensor, void* data) {
// read sensor data into data buffer
// ...
for (size_t i = 0; i < sizeof(sensorStrategies) / sizeof(sensorStrategies[0]); i++) {
if (sensorStrategies[i].sensor == sensor) {
sensorStrategies[i].strategy(data);
break;
}
}
在這個例子中,我們使用一個策略表來存儲不同的策略對象,每個對象包含一個傳感器編號、數(shù)據(jù)大小和一個策略函數(shù)指針。當(dāng)某個傳感器的數(shù)據(jù)被讀取時,函數(shù)會根據(jù)傳感器編號查找策略表,找到相應(yīng)的策略并執(zhí)行。
總結(jié)一下,嵌入式系統(tǒng)中使用策略模式可以將復(fù)雜的業(yè)務(wù)邏輯分解成多個小的算法,每個算法都可以單獨開發(fā)、測試和維護。通過使用策略表,我們可以方便地管理這些算法,并在運行時動態(tài)地選擇合適的算法。這種設(shè)計方式可以提高代碼的可讀性、可維護性和可擴展性,是嵌入式系統(tǒng)開發(fā)中常用的設(shè)計模式之一。
下面我們來看一個更加具體的例子。假設(shè)我們正在開發(fā)一個智能家居系統(tǒng),其中有多個設(shè)備(如燈光、窗簾、溫度傳感器等),每個設(shè)備都有多種操作模式(如開關(guān)、調(diào)節(jié)亮度、調(diào)節(jié)溫度等),我們需要根據(jù)用戶的輸入來選擇相應(yīng)的操作模式。這時候就可以使用策略模式。
首先,我們定義一個設(shè)備基類和一個操作模式基類,用于后續(xù)的派生類實現(xiàn):
typedef struct _Device Device;
typedef struct _Mode Mode;
struct _Device {
uint8_t id;
char* name;
Mode* mode;
};
struct _Mode {
char* name;
void (*Do)(Device*);
};
接下來,我們定義幾個設(shè)備派生類和操作模式派生類
typedef struct _Light Light;
typedef struct _Curtain Curtain;
typedef struct _Thermostat Thermostat;
struct _Light {
Device base;
bool state;
uint8_t brightness;
};
struct _Curtain {
Device base;
bool state;
uint8_t position;
};
struct _Thermostat {
Device base;
float temperature;
Mode* modes[3];
};
void Light_On(Device* device) {
Light* light = (Light*)device;
light->state = true;
printf("%s turned on.\\n", light->base.name);
}
void Light_Off(Device* device) {
Light* light = (Light*)device;
light->state = false;
printf("%s turned off.\\n", light->base.name);
}
void Light_Dim(Device* device) {
Light* light = (Light*)device;
light->brightness--;
printf("%s dimmed to %d%% brightness.\\n", light->base.name, light->brightness);
}
void Light_Brighten(Device* device) {
Light* light = (Light*)device;
light->brightness++;
printf("%s brightened to %d%% brightness.\\n", light->base.name, light->brightness);
}
void Curtain_Open(Device* device) {
Curtain* curtain = (Curtain*)device;
curtain->state = true;
printf("%s opened.\\n", curtain->base.name);
}
void Curtain_Close(Device* device) {
Curtain* curtain = (Curtain*)device;
curtain->state = false;
printf("%s closed.\\n", curtain->base.name);
}
void Curtain_Up(Device* device) {
Curtain* curtain = (Curtain*)device;
curtain->position++;
printf("%s raised to %d%% position.\\n", curtain->base.name, curtain->position);
}
void Curtain_Down(Device* device) {
Curtain* curtain = (Curtain*)device;
curtain->position--;
printf("%s lowered to %d%% position.\\n", curtain->base.name, curtain->position);
}
void Thermostat_Cool(Device* device) {
Thermostat* thermostat = (Thermostat*)device;
thermostat->temperature--;
printf("%s cooled to %.1f°C.\\n", thermostat->base.name, thermostat->temperature);
}
void Thermostat_Heat(Device* device) {
Thermostat* thermostat = (Thermostat*)device;
thermostat->temperature++;
............
..........
...
...
最后,我們在主函數(shù)中使用策略模式進行設(shè)備控制:
int main() {
Light light = {
.base = { .id = 1, .name = "Living room light", .mode = NULL },
.state = false,
.brightness = 100
};
Curtain curtain = {
.base = { .id = 2, .name = "Bedroom curtain", .mode = NULL },
.state = false,
.position = 0
};
Thermostat thermostat = {
.base = { .id = 3, .name = "Kitchen thermostat", .mode = NULL },
.temperature = 20.0
};
Light_On(&(light.base));
Light_Dim(&(light.base));
Curtain_Open(&(curtain.base));
Curtain_Up(&(curtain.base));
Thermostat_SetModes(&thermostat);
thermostat.mode = &(thermostat.modes[0]);
thermostat.mode->Do(&(thermostat.base));
return 0;
}
在這個例子中,我們首先定義了一個設(shè)備基類和一個操作模式基類,并定義了幾個設(shè)備派生類和操作模式派生類。然后,我們?yōu)槊總€設(shè)備添加了一個指向操作模式的指針,以便在運行時動態(tài)切換操作模式。最后,我們在主函數(shù)中使用策略模式進行設(shè)備控制,首先設(shè)置了每個設(shè)備的初始狀態(tài),然后依次執(zhí)行了多種操作模式。
評論
查看更多