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;
}
精彩评论