2016-06-14 2 views
1

У меня есть вопрос относительно наследования и проектирования пользовательского интерфейса.Наследование и 2 типа интерфейса

У меня есть class KeyboardKey, которая представляет собой отдельные клавиши клавиатуры, например, Q, W, E, R, ... и т.д.

У меня есть class Keyboard, который содержит векторclass Keyboardkey. [Важно!]

Я использую SFML, и поэтому каждый раз, когда событие генерируется из цикла событий, оно отправляется классу клавиатуры. Затем этот класс управляет этим событием до соответствующего ключа.

Кроме того, у меня есть class SynthesizerKey, который наследует от KeyboardKey. В дополнение к обычным ключевым словам, таким как «включен ключ», «нажата клавиша», этот класс содержит данные и функции для обработки генерации звукового сигнала sin. Переменные включают амплитуду и текущую фазу волны sin.

Я сейчас собираюсь создать class SynthesizerKeyboard. Я собирался скопировать и вставить весь код из class Keyboard в этот класс, однако это не очень хорошая практика программирования, так как код дублируется!

Основная проблема, которую я имею, заключается в том, что SynthesizerKeyboard содержит функцию для генерации последовательности выборок, которые должны храниться в буфере. Чтобы сгенерировать образцы, цикл повторяется по каждому KeyboardKey и проверяет, нажата ли она. Если это так, тогда мы должны сгенерировать образец, соответствующий этой ноте/частоте клавиш.

Однако, поскольку вектор содержит class KeyboardKey и неclass SynthesizerKey не имеют переменные для фазы и амплитуды волн греха в качестве данных членов элементов вектора.

Я думаю, что мне, возможно, придется выполнять так называемый «рефакторинг» [?] И отделять части «sin wave» от SynthesizerKey от KeyboardKey частей. Другими словами, я выбрасываю класс SynthesizerKey и имею класс Synthesizer и класс KeyboardKey, отдельно. Затем у меня есть вектор Synthesizer в class SynthesizerKeyboard в дополнение к вектору KeyboardKey в class Keyboard, к которому у меня есть доступ в SynthesizerKeyboard через наследование.

Однако это менее элегантно. Есть ли другой способ?

Ниже приведен код, который может помочь читателю лучше понять quesiton.

SynthesizerKeyboard

class SynthesizerKeyboard : public Keyboard 
{ 

public: 

    SynthesizerKeyboard(const sf::Font& sf_font) 
     : Keyboard(sf_font) 
    { 
    } 

    double Sample() const 
    { 
     for(std::vector<KeyboardKey>::iterator it = m_keyboardkey.begin() 
      it != m_keyboardkey.end(); ++ it) 
     { 
      if(it->IsKeyPressed()) 
      { 
       it->Sample(); 
      } 
     } 
    } 

    void GenerateBufferSamples(std::vector<sf::Int16> buffer) 
    { 
     for(std::size_t i = 0; i < buffer.size(); ++ i) 
     { 
      buffer[i] = Sample(); 
     } 
    } 

}; 

SynthesizerKey

class SynthesizerKey : public KeyboardKey 
{ 

protected: 

    AbstractOscillator *m_abstractoscillator; 

public: 

    double Sample() const 
    { 
     return m_abstractoscillator->Sample(); 
    } 

}; 

Клавиатура

class Keyboard 
{ 

protected: 

    std::vector<KeyboardKey> m_keyboardkey; 

public: 

    Keyboard(const sf::Font& sf_font) 

    void Draw(sf::RenderWindow& window) 

    void Event(const sf::Event& event) 
    { 
     for(std::vector<KeyboardKey>::iterator it = m_keyboardkey.begin(); 
      it != m_keyboardkey.end(); ++ it) 
     { 
      (*it).Event(event); 
     } 
    } 

    bool IsKeyPressed(const sf::Keyboard::Key& sf_key) 
    { 
     for(std::vector<KeyboardKey>::iterator it = m_keyboardkey.begin(); 
      it != m_keyboardkey.end(); ++ it) 
     { 
      if((*it).Key() == sf_key) 
      { 
       return (*it).IsKeyPressed(); 
      } 
     } 
    } 

}; 

KeyboardKey

class KeyboardKey 
{ 

protected: 

    KeyState m_keystate; 
    sf::Color m_pressed_color; 
    sf::Color m_release_color; 
    sf::Text m_sf_text; 
    sf::Keyboard::Key m_sf_keyboard_key; 
    sf::RectangleShape m_sf_rectangle; 

public: 

    KeyboardKey(const sf::Keyboard::Key& sf_keyboard_key, const std::string& text, const sf::Font& sf_font, 
       const double position_x, const double position_y) 

    void Draw(sf::RenderWindow& window) 

    void Event(const sf::Event& event) 

    bool IsKeyPressed() 

}; 
+0

вы можете полочки кода, который вы беспокоитесь о дублировании в «AbstractKeyboard» в комплекте с функциями чистой виртуальной специализации, реализованными «наследниками» AbstractKeyboard, которые вызывается из «AbstractKeyboard», когда требуется, например, «поведение, зависящее от Moog».'AbstractKeyboard' также содержит' std :: vector > ', так что' KeyboardKey' или соответствующие подклассы могут содержаться без проблем управления памятью. – user4581301

+0

@ user4581301 Я не уверен, что я полностью понимаю ваше предложение в деталях ... не могли бы вы объяснить это дальше мне или, возможно, добавить предлагаемый ответ, возможно, с примерами? – user3728501

ответ

0

Переход пропустить ключи на данный момент, но вы должны рассмотреть что-то похожее на них.

Сначала определяют голые кости аннотацию, которая выполняет все общие задачи и включает в себя крючки для классов, которые специализируются абстрактные для заполнения, чтобы выполнить их специфическое поведение:

class AbstractKeyboard 
{ 
protected: 
    std::vector<std::unique_ptr<KeyboardKey>> m_keyboardkey; 
    void Draw(); 
    void Event() 
    { 
     for(auto &key: m_keyboardkey) 
     { 
      key->Event(); 
     } 
    } 

    bool IsKeyPressed(const sf::Keyboard::Key& sf_key) 
    { 
     for(auto &key: m_keyboardkey) 
     { 
      if(key->isKey(sf_key)) 
      { 
       return key->IsKeyPressed(); 
      } 
     } 
     return false; // need this to handle the no match case, otherwise undefined behaviour 
    } 
    void doStuff() 
    { 
     // generic keyboard stuff goes here 
     doSpecificStuff(); 
    } 
    virtual void doSpecificStuff() = 0; 
public: 
    AbstractKeyboard(const sf::Font& sf_font); 
    virtual ~AbstractKeyboard(); 
}; 

Все клавиатуры имеют ключи, поэтому вектор ключей идет здесь. Обратите внимание, что мы перешли от vector ключей к vector интеллектуальных указателей на клавиши. Теперь у нас может быть любой ключ, который наследует базовый ключ, например, ключ синтезатора, а интеллектуальный указатель устраняет обычные проблемы с памятью с указателями.

Большая вынос здесь doStuff функция. Это делает вещи. Что с тобой. Когда это делается, делая основной материал, который должны выполнять все клавиатуры, он вызывает doSpecificStuff, функцию, которую должен заполнить каждый наследник, даже если он ничего не делает. doSpecificStuff делает то, что наследует по-другому, добавляет дополнительные поведения и, как правило, делает синтезатор больше, чем просто обычной клавиатурой.

Вот основная клавиатура:

class Keyboard:public AbstractKeyboard 
{ 
protected: 
    void doSpecificStuff() 
    { 
     // Do keyboard Stuff, if there is any specific stuff to do 
    } 
public: 
    Keyboard(const sf::Font& sf_font): AbstractKeyboard(sf_font) 
    { 

    } 

}; 

Он ничего особенного не делать, но это мог, но поставить специальный код в doSpecificStuff.

синтезатор добавляет несколько функций для народа, которые знают, что это синтезатор (Sample и GenerateBufferSamples), а также реализации doSpecificStuff сделать синтезатор вещи.

class SynthesizerKeyboard : public AbstractKeyboard 
{ 
protected: 
    void doSpecificStuff() 
    { 
     // I do specific stuff! Yay me! 
    } 

public: 

    SynthesizerKeyboard(const sf::Font& sf_font): AbstractKeyboard(sf_font) 
    { 

    } 

    // leaving these as an example of how to get a SynthesizerKey out of m_keyboardkey 
    double Sample() const 
    { 
     // just going to sum because I don't know what's supposed to happen in here 
     double sum = 0.0; 
     for(auto &key: m_keyboardkey) 
     { 
      if(key->IsKeyPressed()) 
      { 
       if(SynthesizerKey* skey = dynamic_cast<SynthesizerKey*>(key.get())) 
       { 
        sum += skey->Sample(); 
       } 
       else 
       { 
        // like freak out, man. 
       } 
      } 
     } 
     return sum; 
    } 

    void GenerateBufferSamples(std::vector<sf::Int16> buffer) 
    { 
     for(sf::Int16 & val: buffer) 
     { 
      val = Sample(); 
     } 
    } 

}; 

Поскольку синтезатор использует ключи синтезатора, Sample содержит пример того, как превратить указатель на обычный ключ в ключ синтезатора и улавливать ошибки конфигурации, которая ставит неправильный тип ключа в m_keyboardkey

добавив виртуальный деструктор и виртуальное ключевое слово в Sample в SynthesizerKeyboard, мы могли бы также сделать

class MoogSynthesizer: public SynthesizerKeyboard 
{ 
public: 
    double Sample() const override 
    { 
     // I do Moog sampling!    
    } 
} 
Смежные вопросы