Nella mia applicazione ho diversi gestori di eventi che eseguono alcune azioni in risposta a eventi dell'interfaccia utente come un clic del pulsante o una selezione di menu. Il codice in questi gestori di eventi è simile a questo ad esempio:
void MyDialog::OnCommandLoadFile()
{
char strFilter[] = { "Text Files (*.txt)|*.txt|All Files (*.*)|*.*||" };
CFileDialog fileDlg(TRUE, ".txt", NULL, 0, strFilter);
if( fileDlg.DoModal() == IDOK )
{
const std::string path = fileDlg.GetPathName();
std::vector<std::string> urls;
int maxNumberOfUrls = settings_->Get<int>(Settings::MaxNumberOfUrls);
UrlProvider *urlProvider = new FileUrlProvider(path);
urlProvider->GetUrls(urls, maxNumberOfUrls);
webMonitoringService_->MonitorUrls(urls);
DisplayMessage("URLs loaded.");
}
}
Questo è tipico dei miei gestori di eventi. Non codifico i gestori di eventi monolitici, ma "incollano" vari pezzi insieme. Di solito ho alcune classi di business logic / servizi a cui l'interfaccia utente fa riferimento e nei gestori di eventi faccio chiamate a uno o più di questi per eseguire l'attività desiderata.
Quello che mi chiedo è che è ancora troppo accoppiato? Se volessi trasformarlo in un'applicazione da riga di comando dovrei estrarre frammenti dal codice dell'interfaccia utente. Nell'esempio sopra, c'è un'azione composta in corso, quindi dovrei pensare che dovrebbe assomigliare più:
void MyDialog::OnCommandLoadFile()
{
char strFilter[] = { "Text Files (*.txt)|*.txt|All Files (*.*)|*.*||" };
CFileDialog fileDlg(TRUE, ".txt", NULL, 0, strFilter);
if( fileDlg.DoModal() == IDOK )
{
const std::string path = fileDlg.GetPathName();
application->MonitorUrlsFromFile(path);
DisplayMessage("URLs loaded.");
}
}
È giusto? Nel codice sopra che cosa dovrebbe essere "applicazione"? Un controller? È la giusta astrazione per questo? Ogni volta che ho bisogno di eseguire qualche compito in un gestore dell'interfaccia utente, dovrebbe sempre essere un one-liner (a parte ottenere dati dentro o fuori dalle caselle di testo, ecc.). Tutti hanno un puntatore a un tutorial che copre davvero questo (in qualsiasi linguaggio di programmazione )?
Inoltre, se in effetti dovresti astrarre azioni composte, come propagare i messaggi di errore dall'interno dell'astrazione composta per tornare all'interfaccia utente? Una gerarchia di eccezioni? Singola eccezione con codice di errore?
Aggiorna
Sto guardando il modello di comando che sembra promettente.
UPDATE 2
Alla fine ho deciso di refactoring il mio codice come descritto nel documento Humble Dialog Box nella risposta accettata. La mia comprensione è che questo è un modello di tipo Model View Presenter (MVP) e più specificamente una vista passiva. Il refactoring sta andando liscio. Ci sono alcuni spigoli nella mia comprensione della piena applicazione del pattern quando si tratta di cose come visualizzare finestre di messaggio e finestre di dialogo aperte o gestire i timer di eventi, ma nel complesso sono più contento del mio codice; l'accoppiamento stretto mi dava fastidio.
Ho trovato anche questo "pacchetto": modelli & pratica Guida allo sviluppatore del client Web: pacchetto Model View Presenter (MVP) che contiene codice MVP di esempio. È un progetto web mentre la mia attuale implementazione è desktop ma credo che la bellezza di MVP sia che in parte non importa. Una cosa che ho ottenuto è stata quella di iniettare i miei oggetti del livello di servizio nel presentatore piuttosto che introdurli nella mia classe di presentazione.