2016-06-14 3 views
1

상속 및 사용자 인터페이스 디자인과 관련하여 질문이 있습니다. 상속 및 2 개의 인터페이스 유형

가 나는 class Keyboardkey벡터를 포함하는 class Keyboard있는 등 Q, W, E, R, ...

같은 개별 키보드 키를 나타내는 class KeyboardKey있다. [중요!]

저는 SFML을 사용하고 있으므로 이벤트 루프에서 이벤트가 생성 될 때마다 키보드 클래스로 보내집니다. 그런 다음이 클래스는 해당 이벤트를 해당 키로 만듭니다.

또한 KeyboardKey을 상속받은 class SynthesizerKey이 있습니다. "키 사용 가능", "키 누름"과 같은 일반 키 스터 외에도이 클래스에는 사인파 톤 생성을 처리하는 데 필요한 데이터와 함수가 포함되어 있습니다. 변수에는 sin 파의 진폭 및 현재 위상이 포함됩니다.

지금은 class SynthesizerKeyboard을 만들려고합니다. 코드를 복사 할 때 좋은 프로그래밍 연습이 아니지만이 클래스에 class Keyboard의 모든 코드를 복사하여 붙여 넣으 려하고있었습니다.

내가 가진 주된 문제는 SynthesizerKeyboard에 버퍼에 저장할 샘플 시퀀스를 생성하는 기능이 있다는 것입니다. 샘플을 생성하기 위해 루프는 각 KeyboardKey을 반복하고 누를 지 확인합니다. 그렇다면, 우리는 해당 키 노트/주파수에 해당하는 샘플을 생성해야합니다. 그러나

상기 벡터와 class KeyboardKeyclass SynthesizerKey 내가 벡터 요소의 멤버 데이터로서 죄 파의 위상 및 진폭에 대한 변수를 갖고 있지 아니 포함하기 때문이다.

"리팩토링"[?]으로 알려진 것을 수행해야 할 수도 있고 부분에서 SynthesizerKey의 "sin wave"부분을 분리해야한다고 생각합니다. 즉, 나는 SynthesizerKey 클래스를 버리고 Synthesizer 클래스와 KeyboardKey 클래스를 따로 가지고있다. 그런 다음 Synthesizer의 벡터가 KeyboardKeyclass KeyboardSynthesizerKeyboard에서 상속을 통해 액세스 할 수있는 것 이외에 class 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

A는 당신이 복제에 대한 걱정 만들 수 '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에 특수 코드를 넣어.

신디사이저는 신디사이저 (SampleGenerateBufferSamples)를 알고있는 민속 용으로 신디사이저 항목을 수행하기 위해 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

로 키의 잘못된 유형을 배치 구성 오류로 일반 키에 대한 포인터를 설정하는 방법의 예를 포함 가상 소멸자와 SynthesizerKeyboardSample에 가상 키워드를 추가함으로써, 우리는 또한 당신은 코드를 구획 할 수

class MoogSynthesizer: public SynthesizerKeyboard 
{ 
public: 
    double Sample() const override 
    { 
     // I do Moog sampling!    
    } 
}