Разрабатываю класс для подключения к LCD (жидкокристаллическому дисплею). Он может подключаться в двух режимах: 4-битный и 8-битный. А количество режимов может быть увеличено. Режим можно настроить в конструкторе. Многое зависит от режима. Но! Режим можно изменить во время выполнения, поэтому я не могу создать Lcd
абстрактный класс и две производные FourBitLcd
и EightBitLcd
. Код:
class Lcd {
public:
enum Mode {
Mode4bit,
Mode8bit,
};
explicit Lcd(Mode mode) : mode(mode) {}
public:
void Init() {
if (this->mode == Mode8bit) {
SendCommand(LCD_CMD_8_BIT_MODE);
}
else {
this->Reset();
SendCommand(LCD_CMD_4_BIT_MODE);
}
}
void SendCommand(uint8_t command_id) {
if (this->mode == Mode8bit) {
this->SendLowerNibble(command_id);
}
else {
this->SendHigherNibble(command_id);
this->SendCmdSignals();
command_id <<= 4;
}
this->SendHigherNibble(command_id);
this->SendCmdSignals();
}
void SendData(uint8_t data) {
if (this->mode == Mode8bit) {
this->SendLowerNibble(data);
}
else {
this->SendHigherNibble(data);
this->SendDataSignals();
data <<= 4;
}
this->SendHigherNibble(data);
this->SendDataSignals();
}
private:
void BusyCheck() {
// ...
if (this->mode == Mode4bit) {
en.Write(true);
_delay_us(10);
en.Write(false);
_delay_us(10);
}
// ...
}
void Reset();
void SendHigherNibble(uint8_t val);
void SendLowerNibble(uint8_t val);
void SendCmdSignals();
void SendDataSignals();
private:
Mode mode = Mode8bit;
};
В этом случае, я думаю, мне следует использовать полиморфизм вместо if / else. Но тут у меня другая проблема. Например, я могу создать ModeStrategy
абстрактный класс и FourBitModeStrategy
и EightBitModeStrategy
. Но производным классам нужен экземпляр класса Lcd
потому что им нужно использовать его методы, такие как SendLowerNibble
и SendHigherNibble
. Но если я передам указатель Lcd
производным от ModeStrategy
тогда это будет двунаправленная ссылка. Насколько я знаю, это нехорошо. Например, код:
class Lcd {
public:
class ModeStrategy {
public:
void InitLcdPtr(Lcd *lcd) {
this->lcd_ = lcd;
}
virtual void InitMode() = 0;
virtual void SendCommandPart(uint8_t &command_id) = 0;
virtual void SendDataPart(uint8_t &data) = 0;
virtual void BusyCheck() {}
protected:
Lcd *lcd_ = nullptr;
};
class FourBitModeStrategy : public ModeStrategy {
public:
void InitMode() override {
this->lcd_->Reset();
this->lcd_->SendCommand(LCD_CMD_4_BIT_MODE);
}
void SendCommandPart(uint8_t &command_id) override {
this->lcd_->SendHigherNibble(command_id);
this->lcd_->SendCmdSignals();
command_id <<= 4;
}
void SendDataPart(uint8_t &data) override {
this->lcd_->SendHigherNibble(data);
this->lcd_->SendDataSignals();
data <<= 4;
}
};
class EightBitModeStrategy : public ModeStrategy {
public:
void InitMode() override {
this->lcd_->SendCommand(LCD_CMD_8_BIT_MODE);
}
void SendCommandPart(uint8_t &command_id) override {
this->lcd_->SendLowerNibble(command_id);
}
void SendDataPart(uint8_t &data) override {
this->lcd_->SendLowerNibble(data);
}
};
friend FourBitModeStrategy;
public:
enum Mode {
Mode4bit,
Mode8bit,
};
explicit Lcd(ModeStrategy *mode_strategy) : mode_strategy(mode_strategy) {
// Bidirectional reference.
this->mode_strategy->InitLcdPtr(this);
}
public:
void Init() {
this->mode_strategy->InitMode();
}
void SendCommand(uint8_t command_id) {
this->mode_strategy->SendCommandPart(command_id);
this->SendHigherNibble(command_id);
this->SendCmdSignals();
}
void SendData(uint8_t data) {
this->mode_strategy->SendDataPart(data);
this->SendHigherNibble(data);
this->SendDataSignals();
}
private:
void BusyCheck() {
// ...
this->mode_strategy->BusyCheck();
// ...
}
void Reset();
void SendHigherNibble(uint8_t val);
void SendLowerNibble(uint8_t val);
void SendCmdSignals();
void SendDataSignals();
private:
ModeStrategy *mode_strategy = nullptr;
};
Но, как я уже сказал, существует двунаправленная ссылка. Как это решить? Заранее спасибо!
1 ответ
Избегайте полиморфизма в этом случае
Вы можете избежать циклической ссылки, передав Lcd *
для каждой функции-члена ModeStrategy
, поэтому последний не должен хранить этот указатель. Однако, учитывая опубликованный вами код, я не думаю, что в данном случае использование полиморфизма является хорошей идеей. Он включает разыменование указателя и косвенные вызовы функций, когда все, что вам нужно, это несколько if (mode == ...)
.
Постарайтесь минимизировать количество функций, которым необходимо знать mode
Возможно, у вас будет всего одна функция, которая позаботится об отправке полного байта, будь то команда или байт данных? Например:
void SendByte(uint8_t data, bool command) {
if (mode == Mode8bit) {
// write data
// write command or data signals
} else {
// write data
// write command or data signals
// write data << 4
// write command or data signals
}
}
потом SendCommand()
и SendData()
и даже Init()
может позвонить SendByte()
без необходимости знать режим.
Ненужное использование this->
Вам почти никогда не нужно явно писать this->
в C ++. Вы даже не делаете это постоянно, например, в Init()
в неполиморфной версии, которую вы называете SendCommand()
без this->
.