Структура кода C ++, подобная переключателю

Разрабатываю класс для подключения к 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 ответ
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->.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *