问题 多个相同的实例对象,其中一部分需要执行某个相同操作。例如有4路天线(使用tx代替),在不同应用中可能选择其中某几路天线,执行“上电-发射-下电”的操作序列。
解决方案 在面向对象的语言中可以实例化多个对象,对需要执行操作的对象分别调用其方法即可。但是在面向过程的C语言中,首先很容易想到的是定义不同的操作函数,将操作哪几个对象作为参数传递。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 typedef enum { TX_CH_1, TX_CH_2, TX_CH_3 } tx_channel_t ; void power_on_action (tx_channel_t tx) { switch (tx) { case TX_CH_1: break ; case TX_CH_2: break ; case TX_CH_3: break ; } } void tx_power_on (bool tx1, bool tx2, bool tx3) { if (tx1) { power_on_action(TX_CH_1); } if (tx2) { power_on_action(TX_CH_2); } if (tx3) { power_on_action(TX_CH_3); } }
当对象少于4个时,这种方式很直接高效,但是当对象多于4个时就存在大量重复代码,并且函数参数过长,需要重构设计了。 考虑到某个对象的参数只需要Yes/No两种值,可以想到使用1 bit代表一个对象。如此一个Byte即可表示8个对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void power_on (uint8_t tx_mask) { for (uint8_t i = 0 ; i < 8 ; i++) { uint8_t sel = 1 << i; if (0 != (tx_mask & sel)) { power_on_action(i); } } } void power_off (uint8_t tx_mask) { for (uint8_t i = 0 ; i < 8 ; i++) { uint8_t sel = 1 << i; if (0 != (tx_mask & sel)) { power_off_action(i); } } } void send (uint8_t tx_mask) { for (uint8_t i = 0 ; i < 8 ; i++) { uint8_t sel = 1 << i; if (0 != (tx_mask & sel)) { send_action(i); } } }
观察操作函数结构均相同,再进一步重构,将重复的结构抽出来,将操作转为参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static inline void for_each_tx (uint8_t mask, void (*action)(tx_channel_t tx)) { for (uint8_t i = 0 ; i < 8 ; i++) { if (0 != (tx_mask & (1 << i))) { action(i); } } } void power_on (uint8_t tx_mask) { for_each_tx(tx_mask, power_on_action); } void power_off (uint8_t tx_mask) { for_each_tx(tx_mask, power_off_action); } void send (uint8_t tx_mask) { for_each_tx(tx_mask, send_action); }
回顾此设计模式,核心原则仍是DRY(Don’t Repeat Yourself),只不过在这里Repeat的不仅是操作,还包括代码结构。