Newsgroup: it.comp.lang.c++
Titolo: [Semi OT][DESIGN] Separazione Implementazione [LUNGHETTO]

Salve a tutti,
vi scrivo per chiedere umilmente aiuto su un piccolo problema di analisi
che sto affrontando in questi giorni.
Sto cercando di sviluppare una serie di classi interfaccia che
mi permettano di astrarre le librerie GUI e permettermi di poter variare 
l'implementazione in seguito.
Inizialmente avevo pensato al Pattern Bridge ma non avendo l'esigenza di
variare l'implementazione a runtime ho optato per l'ereditarietà privata.
Ecco un piccolo esempio di come l'ho pensata:
// Interfaccia globale per tutte le finestre (astratta)
class Window
{
	public:
		virtual ~Window() {}
};
// Un esempio di una classe Dialog
class Dialog : public virtual Window, private DialogImplementation
{
	public:
		Dialog( Window* parent ) : 								DialogImplementation( parent ) {}
		virtual ~Dialog() {}
}
"DialogImplementation" dovrebbe essere la classe che implementa
realmente il Dialog attraverso le librierie della GUI che utilizzo
(Windows, Linux, etc. etc.).
"Dialog" è solo un esempio e tutte le classi che rappresentano un
elemento GUI dovrebbero seguire questa struttura.
Purtroppo ho un grosso problema che non riesco a risolvere e riguarda ad
esempio il costruttore di Dialog.
In quasi tutte le librerie GUI ogni controllo accetta come costruttore
una finestra padre e, utilizzando Window come interfaccia comune, non so 
come passare questo dato alle librerie
d'implementazione, provo a fare un esempio:
Poniamo che per l'implementazione della GUI utilizzassi la libreria
QT...avrei una situazione del genere:
class DialogImplementation : public QDialog
{
	public:
		DialogImplementation( Window* parent )
			: QDialog( parent ){}
};
Ovviamente così non funziona perchè la QDialog si aspetta come argomento 
un puntatore alla classe QWidget (la radice di tutte le finestre QT).
Al momento l'unica soluzione che trovato è la riscrittura della classe 
Window così:
class QWidget;
typedef WindowBase QWidget;
class Window
{
	public:
		virtual ~Window() {}
		virtual WindowBase* base() = 0;
};
In questo modo ogni classe d'implementazione restituisce la finestra 
base. Il costruttore di QDialog diventa "QDialog( parent->base() )".
La soluzione non mi convince. Window perde la sua funzione di 
interfaccia indipendente dalla piattaforma utilizzata.
Qualcuno di voi ha qualche idea su una soluzione per risolvere questo 
problema?
Grazie per la pazienza...
Ciao a tutti
ps. Lo so che Qt è già una libreria GUI indipendente dalla piattaforma 
ma è la prima che mi è venuta in mente :).

 problema?
Una soluzione ce l'avrei, ma non e' molto ortodossa. Anzi a dire la
verita' e' veramente complicata e non so se possa interessarti, ma
comunque la posto visto che finora nessun altro ti ha risposto.
L'idea e' la seguente:
- Astrai la creazione dei widget con una Abstract Class Factory (e.g.
class AbstractWidgetFactory).
- Implementi per ogni libreria GUI una classe derivata dalla factory
astratta (e.g. class QTWidgetFactory).
- Nell'implementazione della factory mantieni una relazione tra le
Window e i QWidget creati. Questo lo puoi realizzare con una
std::map<Window*, QWidget*> e tre funzioni pubbliche:
(1) void register_window(Window* w, QWidget* qw) che aggiunge una
coppia alla map
(2) void release_window(Window* w) che rimuove una coppia dalla map
(3) QWidget* get_window_base(Window* w) che ritorna il QWidget*
corrispondente a un Window*
- DialogImplementation non deriva solo da QDialog ma anche da una
classe astratta IBaseWidget che espone due metodi virtuali protetti:
(1) void register(Window* w)
(2) void release(Window* w)
- DialogImplementation implementa questi due metodi ottenedo un
puntatore alla QTWidgetFactory e chiamando register_window(...) e
release_window(...).
- Allo stesso modo DialogImplementation nel costruttore ottiene un
puntatore alla QTWidget factory e chiama get_window_base(...) per
ottenere il puntatore a Window da passare al costruttore di QDialog.
- Infine la classe Dialog chiama nel costruttore la funzione
register(...) e nel distruttore la funzione release(...)
In questo modo si nasconde la specializzazione della GUI nelle classi
QTWidgetFactory e DialogImplementaion, lasciando completamente
generiche le classi Dialog e Window. L'impatto del uso di std::map
dovrebbe essere trascurabile.
Per chiarezza, posto uno scheletro del codice.
class AbstractWidgetFactory
{
public:
    virtual Dialog* create_dialog(...) = 0;
    virtual ComboBox* create_combobox(...) = 0;
    ...
};
class QTWidgetFactory : public AbstractWidgetFactory
{
private:
    QTWidgetFactory();
    static QTWidgetFactory* factory;
    std::map<Window*, QWidget*> m_window_map;
public:
    virtual Dialog* create_dialog(...) {...};
    virtual ComboBox* create_combobox(...) {...};
    static QTWidgetFactory* get()
    {
        if(!factory)
            factory = new QTWidgetFactory();
        return factory;
    }
    QWidget* get_window_base(Window* w)
    {
        return m_window_map[w];
    }
    void register_window(Window* w, QWidget* qw)
    {
        m_window_map[w] = qw;
    }
    void release_window(Window* w)
    {
        ...;
    }
};
class Dialog : public virtual Window, private DialogImplementation
{
public:
    Dialog( Window* parent ) :
        DialogImplementation(parent)
    {
        register(this);
    }
    virtual ~Dialog()
    {
        release();
    }
};
class IBaseWidget
{
protected:
    virtual void register(Window* w) = 0;
    virtual void release(Window* w) = 0;
};
class DialogImplementation : public QDialog, public IBaseWidget
{
    public:
        DialogImplementation( Window* parent)
            : QDialog(QTWidgetFactory::get()->get_window_base(parent))
        { }
protected:
    virtual void register(Window* w)
    {
        QTWidgetFactory::get()->register_window(w, this);
    }
    virtual void release()
    {
        QTWidgetFactory::get()->release_window(this);
    }
};
Tutto questo non l'ho minimamente testato, quindi potrebbe pure darsi
che violi qualche regola del C++ e che io non me ne sia accorto.
Saluti,
The Librarian