开发者

GUI and Text Mode C++ design to remove redundancy (optional parameter? function overloading?)

Using C++, I have a text mode and a GUI mode implemented using FLTK chosen by command line option and I see a lot of redundancy in the codes except for an extra parameter in the GUI case which I need to pass in the main window widget. I'm wondering if there are ways to remove the redundancy? Maybe I have some design issue completely wrong? I'd appreciate the help and please let me know if there are additional information needed. I have thought of using optional parameter, but cannot take a NULL reference.

Here're a skeleton of the codes that are really similar (Not exact, but should be close enough to see the general structure). There may be some more if/else loops or functions nested before I have the one different call with the extra Window& parameter, but this is ba开发者_Go百科sically the structure, which actually continue a few levels down.

Thanks for any help!

int Game::init(){
  if (graphics){
    std::unique_ptr<Window> window = std::unique_ptr<Window>(new Window(...))
    return Fl::run();
  } else {
    play_game();
    return 0;
  }
}

void Window::init(Fl_Widget* w, void *uData){
    Window* window = (Window*) uData;
    Window->game.play_game(window);
    //Window has a private game& that is constructed to be equal to the game above.
}

void Game::play_game(){
    while(!over()){
       foo();
       bar();
    }
}

void Game::play_game(Window& window){
    while(!over()){
       foo();
       bar(window);
    }
}

void Game::bar(){
   if(!a()){
      b();
   } else {
      c();
   }
}

void Game::bar(Window& window){
   if(!a()){
      b();
   } else {
      c(window);
      window.redraw();
   }
}

A similar but different question deals with how I deal with the static function in FLTK, I have similar code somewhere that is like this:

void Game::c(){
  if(check_this()){
    do_this();
  }
}

void Game::c(Window& window){
  Fl::run();
}

static void Window::call_back(Fl_Widget* w, void* uData){
  Window* window = (Window *) uData;
  if(window->game.check_this()){
    window->do_this();
  }
}


Parameter is not the way to go. Subclassing Game is the way to go. Any method that needs access to the window is virtual and overridden appropriately in the window-specific subclass.

class Game        // I hate K&R braces, sorry
{
public:
    enum GameType { cli, win };
    static Game &GameFactory(GameType gt)
    {
        switch (gt)
        {
        case cli: return /* ref to instance of CliGame() */;
        case win: return /* ref to instance of WinGame() */;
        }
    }

    virtual int launch() = 0;
    void foo();
    void bar()
    {
       if (!a()) { b(); } else { c(); }
    }
    bool a();
    void b();
    virtual void c();
    void play()
    {
        while (!over()) { foo();  bar(); }
    }
private:
    // need some sort of static management of instance of game, how is up to you
};

class WinGame : public Game
{
public:
    virtual int launch()
    {
        window = std::unique_ptr<Window>(new Window(...));
        return Fl::run();    // presumably calls play_game() sometime....
    }
protected:
    virtual void c()
    {
        // does whatever, using window *member* (not argument)
        window.redraw();
    }
private:
    std::unique_ptr<Window> window;
};

class CliGame : public Game
{
    virutal int launch()
    {
        play_game();
        return 0;
    }
    virtual void c()
    {
    // does whatever
    }
};

int main()
{
    Game::GameType graphics;
// 'graphics' gets set somehow
    Game &g = Game::GameFactory(graphics);
    int retval = g.launch();
// etc
}


Can you make window a member of your Game class so that you don't have to pass it around so much?

One way to do this - while still maintaining Game seperate from the windowing - is to use templates. This would involve an interface change, however, and so may not be appropriate.

First of all separate your window specific and console specific code:

//class containing all your window management
class window
{
  window()
  {
    //construct
    std::unique_ptr<Window> m_window = std::unique_ptr<Window>(new Window(...));
  }

  void
  redraw()
  {
    m_window.redraw();
  }
private:
  std::unique_ptr<Window> m_window;
};

//your console management
class console
{
};

Then load up your game with your windowing/console option.
Inherit from your option so that you can use the domain specific functions.

template<WindowOption>
class Game : public WindowOption
{

  void
  play_game()
  {
    while(!over()){
      foo();
      bar();
    }
  }

  void 
  bar() //request for window functions deferred
  {
    if(!a()){
      b();
    } else {
      c();
    }
  }

  void b() 
  {
    //console default
  }
  void c()
  {
    //console default
  }

};

Then specialize the functions that need to call window specific menthods.

template<>
Game<window>::b()
{
  //with window specialisation
  //can call window specific functions here as we have a window.
  redraw();
}


template<>
Game<window>::c()
{
  //with window specialisation
  //can call window specific functions here as we have a window.
  redraw();
}

Then call:

int
main  (int ac, char **av)
{

  Game<window> gw;
  Game<console> gc;

}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜