Newsgroup: it.comp.programmare
Titolo: oop domande

ciao,
 
 
devo realizzare un programma in C# con interfaccia visuale, quindi non 
in console...
 
quando passo i dati di inizializzazione di un oggetto, ad esempio al 
costruttore, mi chiedevo se conveniva da quest'ultimo invocare le 
relative proprietà pubbliche al fine di validare i dati in ingresso 
all'oggetto, o se gestire il controllo dei dati ancor prima di invocare 
il costruttore, ovvero al click di un pulsante, recuperare dalle textbox 
i dati verificarne il valore e solo in caso positivo creare 
l'oggetto.... oppure mostrare una messagebox all'utente per avvisarlo 
della non coerenza di alcuni valori, evitando quindi di istanzaire 
l'oggetto.
 
Nel caso delle proprietà invocate dal costruttore, converrebbe lanciare 
da queste ultime delle eccezioni,mostrando la messagebox invocata dalla 
stessa proprietà, oppure mostrando la messagebox con dei mex senza 
scomodare le eccezzioni?
 
 
grazie


Premetto che non conosco C#, ma visto che il tuo è un problema di design 
e non  di linguaggio te lo spiego lo stesso
Ti premetto inoltre che il design è anche questione di gusti e pur 
esistendo vari pattern per risolvere particolari problemi, non sempre è 
possibile dire con certezza se un modo di procedere è giusto o sbagliato
 
Comunque imposterei il problema cosi:
Supponiamo di avere un attributo X di classe C che deve essere 
verificato prima di essere memorizzato in un oggetto di classe C
Non solo, ma se il valore per X non è valido occorre informare l'utente.
 
Si imposta un metodo per memorizzare il valore nell' oggetto ed un 
metodo di classe per il check del valore
Dopo, dentro il metodo per memorizzare il valore si chiama il check sul 
valore e si lancia una eccezione se il valore non è valido
 
Es:
 
class C  {
	void set_X(T value) {
		assert(check_X(value));
                 ....
	}
	static bool check_X(T Value) { .... }
}
 
 
la parola static significa che il metodo è di classe,in C# sono sicuro 
che esiste qualcosa di analogo
 
assert è un metodo che valuta una espressione booleana e se il risultato 
è falso lancia una eccezione
 
void significa che il  metodo non ritorna valori
 
 
Ora per quanto riguarda il messaggio all' utente si eseguono, prima di 
creare l' oggetto,  i vari test
 
bool err=false;
if (not C::check_X(...)) {
    messagebox(....); // emette il messaggio di errore all' utente
    err=true;
}
  // altri test
 
 
if (not err)  {
	// crea l' oggetto di classe C
 
}
 
 
 
In  questo modo hai due vantaggi
la classe è robusta in quanto non possono entrare valori errati (un test 
è sempre possibile dimenticarlo) e volendo puoi eseguire più test in 
cascata e presentare un solo messaggio all' utente grazie alla 
disaccoppiata check e creazione oggetto
 
 
 
 
 
 

 disaccoppiata check e creazione oggetto
 
thx!

 
 
Non ho capito.
(in ogni caso, così, perché sono un precisino, "eccezioni" è con una z sola e "messaggi" normalmente lo si scrive "msgs", se proprio lo devi accorciare).
 
Ciao!

...la prima volta eccezioni è scritto correttamente....quindi la seconda 
volta possiamo considerarla una svista....
 
 
 

 disaccoppiata check e creazione oggetto
 
I check in cascata hanno senso solo se prendi i valori in cascata, che
e' una cosa atipica ormai sia in un software con GUI che in
un'interfaccia web.
 
Quello che si fa oggi e' validare i parametri indipendentemente (con le
varie features della ui o con una qualche libreria javascript), poi
rivalidarli quando si crea l'oggetto. Ma la funzione delle validazioni
e' diversa!
 
La prima e' una questione di usabilita': vuoi segnalare all'utente un
problema appena si manifesta e lasciarglielo correggere. La seconda e'
una questione di robustezza del software (ovvero si vuole che l'oggetto
costruito sia in uno stato valido).
 
 
-- 
-riko
http://www.enrico-franchi.org/
 
http://rik0-techtemple.blogspot.com/
 


 
 
Che è quello che il modo che ho suggerito fa
 
 

 
 
Secondo me tutti e due state andando un po' fuori strada: è un discorso di
pattern architetturali, non di environment o linguaggi. Vero è che, a seconda
dell'ambiente o linguaggio, alcuni di questi sono già implementati
implicitamente, in altri bisogna fare attenzione.
 
M2C,
Ciao!
 

 Che è quello che il modo che ho suggerito fa
 
No. Non lo fa. Ti lascio per esercizio capire perche'.  ;)


 implicitamente, in altri bisogna fare attenzione.
 
Per me menzionare un paio di tecnologie era solo un modo per fare
suonare qualche campanello nel caso in cui uno lo avesse mai fatto e
collegare quanto dicevo alla sua esperienza. Se uno ha fatto un minimo
di web, probabilmente si e' imbattuto nel problema di validare *anche*
lato client (per offrire maggiore usabilita'), o magari ha letto dei
problemi relativi alla validazione *solo* lato client. Se uno ha usato
un po' una qualche libreria di GUI, magari si e' imbattuto nelle
features che permettono di attaccare un validatore ad un campo. 
 
Non voglio confondere l'architettura, le diverse funzioni logiche dei
due tipi di validazione. Voglio solo richiamare quella che certamente e'
esperienza condivisa per meglio chiarire la questione.


 
 
Sì, certamente: quello che volevo dire è che il codice in questo caso c'entra
poco, è un discorso di "filosofia diffusa".
 
Con un design non monolitico (e non necessariamente "disaccopiato"), l'unico
modo per implementare moduli (o classi, o funzioni, o qualsiasi cosa sia nel
linguaggio scelto) robusti e/o riusabili è fare in modo che ognuno controlli
sempre i propri input. Molto spesso è inutile, ma garantisce un feedback
negativo a valle del cambio del chiamante (se si fanno le cose come scrivo
sotto), sempre che si cerchi di mantenere le risorse necessarie ai check come
una percentuale irrilevante rispetto al lavoro complessivo del modulo
(altrimenti stiamo sovra-ingegnerizzando).
 
A questo punto però nascono tre problemi: chi gestisce gli errori, quando e
come? (per capirci, metto un if o tiro un'eccezione, ma poi?)
 
Le domande sono *molto* complicate, c'è gente che non ci dorme la notte e IMO
il problema generale non è ancora stato risolto in maniera soddisfacente.
 
Secondo il pattern MVC, che va (andava) molto di moda, si dividono i moduli
tra chi fa il lavoro d'interazione con l'utente/ambiente (V), chi fa il "lavoro
sporco" (M) e chi richiama le facility dei due e gestisce controlli ed
eccezioni (C).
Esistono MVA, MVP, PAC, etc.etc. ognuno con pro e contro, ma sempre fanno
uso di moduli (o classi, o funzioni, o etc.) scritte nello stesso modo, con
gli stessi controlli d'errore, negli stessi punti.
 
Quindi niente, questo è un discorso generale applicabile a qualsiasi
linugaggio, anche non OOP.
 
Ciao!


 
 
Contraccambiati
 
 
 
 
 


 
Tante belle parole e tante belle critiche
Ma finora non ho visto una sola riga di codice
 
Quindi lasciamo da parte le belle parole e presentami un codice (o 
pseudocodice)
di come faresti tu
 
 
 

 
 
Probabilmente infatti no, ma a proposito di questo scrivo qualcosa sull'altro
ramo di questo thread. 
 
Ciao!

On Tuesday, July 9, 2013 4:55:05 PM UTC+2, enoquick wrote:
 
[BIIIG CUT]

Lo faccio io.
Faccio un esempio cercando di essere abbastanza fumoso da poter essere esteso
alla maggior parte dei problemi di questo tipo.. sostituite i nomi come volete.
Mettiamo che abbiamo un applicativo con GUI che prende dei dati e restituisce
un output; contemporaneamente salva sia input che output su una base di dati
locale per ritirarli fuori in caso servano.
Bòn, direi che un gran numero di programmi sono così, no?
Cerco di seguire l'MVC, che ho citato, anche se come ho detto a me non piace
molto.
Scriviamo solo le interfacce:
interface View {
    public void setData(data) { /* fill GUI fields with data */ }
    public data getData() { /* call validateData(), and get data from GUI,
                                throws something */ }
    private bool validateData() { /* check for field validity */ }
}
interface Model {
    public data doStuff(data) {/* do whatever and output, throws on error */}
}
/* di questa interfaccia avremo qualcosa tipo: */
class WorkerModel : implements Model {
    public odata doStuff(data) { /* do computations */ }
}
class PersistentModel : implements Model {
    public data doStuff(data) { /* save stuff */ }
}
/* ok, controller */
interface Controller {
    View currentView;
    Array<Model> models;
    public void onUserOk() {
        data userInput, sysOutput;
        try {
            userInput = currentView.getData();
            foreach(Model m in models)
                userInput = m.doStuff(userInput);
            sysOutput = userInput;
            currentView.setData(sysOutput);
        } catch(e) {
            userInput = toError(userInput, sysOutput);
            currentView.setData(userInput);
        }
    }
}
 
Easy and clean, no?
Il perché a me fa ca*are ve lo dico la prossima ;)
 
Ciao!


Easy and clean ?
Hai impostato 3 interfacce e 2 classi, fai un po tu; meno male che 
almeno affermi che ti fa ca*are
E il famoso oggetto costruito valido quale sarebbe ?
 

 E il famoso oggetto costruito valido quale sarebbe ?
 
Non pensavo di doverlo scrivere, ma nel "rolling" del Controller, che fa un
setData del currentView in caso di throw dei costruttori.
 
Sai che stimo molto i suggerimenti che dai su questo ed altri NG, ma così fai
capire che non hai mai mai affrontato nessun problema del genere, almeno non
secondo i pattern "moderni".
 
Respect,
Ciao!


 
 
Ricambio
 
 
 

 Ricambio
 
;)

 
 
Come al solito, bisogna stare attenti a non uccidere la mosca a cannonate.
Per cose semplici non serve, per cose più grandi.. dipende da come la vedi.
 
Ci sono varie cose che questo design rende semplice:
- la separazione tra codice applicativo e flusso: in questo modo con pochi
accorgimenti è più facile andare su multithread o spostare la logica in vari
ambienti o progetti diversi, senza toccare gli algoritmi.
- la separazione tra codice applicativo e di presentazione: visto che hai
le classi applicative separate è più facile riusare la logica o le GUI.
- la centralizzazione della gestione degli errori: rende più facile e veloce
il debug, il logging, etc.
- l'isolamento della parte che parla con l'utente: rende più facile trovare
e gestire problemi di sicurezza derivanti da input sbagliati/maligni.
- più importante di tutti, molti framework sono sviluppati secondo questo
pattern, per cui non solo hai già tanto codice scritto, ma chi prende in mano
il codice sa già dove mettere le mani a seconda del caso senza stare a
leggersi tutto. Per dirne una, nel mondo dello sviluppo web è una cosa che ci
si aspetta.
 
Ciao!

 
 
Sì, il mio "pseudo-codice" fa schifo. :)
 
Faccio un esempio con due pagine web che parlano con un DB, per i posteri e per
i curiosi.
 
Assumendo l'esistenza delle tre interfacce di prima, abbiamo:
class DBModel : implements Model { ... }
class ViewPage1 : implements View { ... }
class ViewPage2 : implements View { ... }
class ControllerPage1 : implements Controller {
    /* usa DBModel e crea/usa ViewPage1 */ }
class ControllerPage2 : implements Controller {
    /* usa DBModel e crea/usa ViewPage2 */ }
 
DBModel può essere un singleton o statica.
ControllerPageX crea e usa il suo ViewPageX e, in chiusura (al passaggio all'
altra pagina), instanzia e passa il controllo all'altro ControllerPage, che
farà la stessa cosa.
 
In questo design avremo varie piccole classi che rappresentano i dati veri e
propri, mettiamo i dati di un utente che si registra: questi saranno, mettiamo,
dentro una apposita classe DataUser.
Questa classe farà parte concettualmente del Model, ma sarà creata/verificata
dal DBModel, visualizzata dal ViewPageX e passata avanti e indietro dal
ControllerPageX.
Nel caso in cui il ViewPageX permettesse l'inserimento di dati che vanno a
finire in questa classe, allora esisterà una DataUserView, creata e validata
dalla ViewPageX, e una funzione di conversione tra DataUserView e DataUser
dentro il ControllerPageX: in questo modo abbiamo due validazioni e una
conversione, ma riusciamo a tenere separati i concetti di dati utente e dati
di modello (cosa che a volte torna addirittura utile, visto che spesso il DB ha
dettagli irrilevanti per la visualizzazione e viceversa).
 
Spero si capisca.. :|
 
In pratica le cose sono un po' più strutturate, ma nemmeno molto: un esempio
molto calzante è la struttura dello Zend Framework, che è permeato in ogni
punto da questo modo di fare.
 
Ciao!

 di come faresti tu
 
Ma scherzi? E' tutta roba di una banalita' sconvolgente... vuoi che ti
scriva un esempietto di gestione delle eccezioni? E' roba da CS101, dai
su. 
 
Minchia, crei un oggetto, il costruttore controlla se i parametri vanno
bene, se no lancia un'eccezione. Qualcuno nello strato opportuno
aggiustera' la cosa oppure presentera' un messaggio opportuno
all'utente. Opportuno vuole dire che il messaggio parla nel mondo degli
utenti, non in quello degli sviluppatori. Verosimilmente l'errore vero
sara' da qualche parte nei log dell'applicazione.


 domanda cadeva a pennello per i pattern nuovi.
 
Poi "nuovi"... di MVC si parlava ai tempi di Smalltalk 76 e posso dire
che e' parte del vocabolario del programmatore medio sicuramente dai
primi 2K.
 
Nota che per me questa non era *nemmeno* una questione di MVC (anche se
ovviamente il contesto si cala), ma semplicemente del fatto che non e'
concepibile avere paccate di righe *fuori* ogni volta che si costruisce
un oggetto. 
 
Anche perche' quelle righe (dall'esempio di enoquick) ricalcano in parte
qualcosa che sarebbe stato esplicitamente o implicitamente nel
costruttore.


 E questo nuovo oggetto deve ereditare da View per i poter eseguire i check
 
No per carita'... fai ereditare un model da una view e sono brutte cose!


 si aspetta.
 
Quoto tutto, ovviamente.


 
 
ehehe, giusto, verissimo! :)
 
Ciao!


 
 
Ottima osservazione
E cosi la classe che riceve i dati si ritrova a dover duplicare il 
metodo validateData, o vogliamo che l' oggetto da costruire possano 
potenzialmente entrare dati non validi ?
 

 costruttore.
 
 
Poi mi dirai come fare a gestire i check_* nell' ordine desiderato da 
chi deve occuparsi di implementare i check_* nel costruttore
 
 

 chi deve occuparsi di implementare i check_* nel costruttore
 
Magari ho perso qualche passaggio, ma non capisco questa cosa dell'"ordine"...
Il costruttore a partire da certi parametri, riesce o fallisce, punto. Che ti
frega di come/quando sono controllati, se quando non sono buoni in ogni caso
va in eccezione?
 
Ciao!

 potenzialmente entrare dati non validi ?
 
Scusa, non mi e' assolutamente chiaro cosa intendi. Secondo me stai
aprendo una casistica infinita e non mi e' chiaro di quale caso
specifico stiamo parlando.
 
Tipicamente la view non necessariamente fa validazione dei dati (anzi,
di per se non la fa[0]). La responsabilita' della view e' *presentare* i
dati. 
 
----
[0] se poi vogliamo entrare nelle specifiche tecnologie, puo' capitare
di tutto... ma alla fine dei conti la validazione dei dati e' parte del
model. Fine della fiera. 


 va in eccezione?
 
Appunto. Per inciso, se si vuole che l'eccezione sia particolarmente
specifica e' ovviamente possibile encodare tutti i problemi dei
parametri all'interno di una singola eccezione. Qualche volta immagino
che possa essere utile, nel caso normale direi che e' un overkill.



 
Stai dicendo che all' utente gli dici semplicemente 'dati non validi' ?
Credo che l' utente voglia sapere quale sia il o i dati non validi
 
 
 
 
 
 
 


Non sto a disquisire se una view debba o no fare la validazione dei dati
Il fatto è che nella view il buon FMassei ha messo il metodo validateData
Solo quello
Ora nella classe che riceverà i dati (quella che ho chiamato C) 
occorrerà duplicare il metodo validateData ?
 
 
 
 
 
 
 

 occorrerà duplicare il metodo validateData ?
 
Visto che sono menzionato, rispondo :)
 
Sì, no, forse.
Il punto fisso è che ognuno valida i dati di sua competenza, quindi...
come ho scritto nell'esempio, dipende dal workflow dei dati: se, ad esempio,
quelli che sono concettualmente gli stessi dati vanno dalla GUI al DB allora
sì, *devono* fare due validazioni: una lato View (che controlla roba tipo che
non metti un campo mandatory vuoto) e una lato Model (che controlla roba tipo
che 
mro...@gmail.com
 è un utente già registrato).
In questo modo ciascuna classe ha sempre i dati sufficenti, anche se a livello
globale non lo sono.
 
Ciao!


Lo spiego in modo semplice
Chi costruisce la classe C è un programmatore
Chi si occupa di presentare gli errori all' utente puo essere un altro 
programmatore (in grossi progetti questo può essere la norma)
Ora o il programmatore che costruisce C deve conoscere l' ordine dei 
vari check_* da effettuare nonchè decidere se lanciare  una sola 
eccezione (una classe) con dentro tutti gli errori che si sono 
presentati o no, oppure a lui non importa nulla, e si occupa solo di 
fare in modo che C faccia quello che per cui deve essere costruita 
nonchè assicurarsi che i dati che riceve siano validi
E' una scelta, ho preferito che non sia il programmatore che costruisce 
  C ad occuparsi di questo
 
 
 

   C ad occuparsi di questo
 
Eh, però è una scelta quantomeno inusuale.
Chi, se non chi sviluppa C, sa quali dati in input sono buoni o no? :)
 
A livello aziendale, l'alternativa che proponi comprende N persone che vanno
da un ufficio all'altro dicendo "ue, guarda che ho cambiato un constraint
perché non ci stavo dentro, aggiungilo ai check!" (e così in cascata per ogni
parametro in by-pass, senza contare le visite di quelli che, costruendo un
utilizzatore nuovo, passano per far presente che vogliono essere avvertiti in
caso di cambi, e senza contare che nessuno scriverà mai i test appropriati o,
se lo fanno, saranno tutti diversi!).
 
Ho un po' esagerato, ma penso neppure troppo.. In effetti mi sono trovato
qualche volta in situazioni del genere :)
 
Ciao!

 che 
mro...@gmail.com
 è un utente già registrato).
 
Si, tipicamente sono cose diverse. Un altro esempio potrebbe essere
scrivere un identificativo di un ordine, un conto e' controllare
sintatticamente che la forma sia giusta, un conto e' controllare che a
quell'id corrisponda un ordine.
 
Comunque, io aggiungo, per me strettamente parlando nella view non c'e'
validazione di nessun tipo, ma esclusivamente presentazione.
 
La gestione dell'input dall'utente e' logicamente roba da controller,
che poi decide come e se intervenire sul model. Nota, non
necessariamente il controller fa validazione e per una serie di ragioni
finisce nella view.
 
Questo dal punto di vista puramente logico. Poi ci sono una serie di
considerazioni pratiche. Per esempio certi framework per UI ti fanno
attaccare dei validtatori a pezzi della UI. Questa e' spesso roba
sintattica (anche se non necessariamente). O c'e' il solito Javascript
nel web... che ovviamente e' li specialmente per motivi di usabilita' e
di efficienza.
 
Poi voglio dire, ci sono creature che ti fanno generare le form web con
javascript in modo semi/automatico con validazione dichiarativa, e il
passo successivo che alcuni hanno e' che essenzialmente scrivi in modo
dichiarativo anche parte del model e poi puoi automaticamente estrarre
quello che vuoi come validazione dell'UI. Pero' questo approccio non
richiede ridondanza: il tutto e' specificato una volta sola, e' il
framework che fa la magia.



Più che altro a seconda di questi constraint di efficenza, o forse meglio 
dire caratteristiche dell'environment, si usano varianti più o meno pesanti
della stessa pappa.
Sinceramente la differenza tra model-view-presenter e model-view-controller,
ad esempio, non è che mi sia mai stata del tutto chiara, anche se il concetto
di base è lo stesso hanno due nomi diversi per qualche motivo e alcuni
framework usano uno o l'altro.
L'ultimo che citi, ad esempio, potrebbe ricordare il presentation-model,
quello dove il framework costruisce il presenter a partire dalla UI e dal
model - da quanto ne so ha ancora un'altro nome perché uno dei pezzi di cui
sopra è sì presente ma trasparente allo sviluppatore.
 
In ogni caso questo sì (il dare un sacco di nomi allo stesso design in base
a piccoli dettagli), secondo me, è complicare le cose: alla fine l'importante
è separare le competenze e poter scrivere unit-tests facilmente, tutto con un
sistema che la maggior parte degli sviluppatori capisce. Il resto mi sembra
molto marketing e poco pratica - o magari la penso così perché non ho mai
studiato veramente a fondo questo tipo di robe.
 
Ciao!

 occorrerà duplicare il metodo validateData ?
 
Chiedilo a lui. 



 
Tu hai giustamente fatto notare che la validazione dei dati non è 
compito della view
Per questo ti ho semplicemente risposto, ma ti ricordo che da te non ho 
ancora avuto l' onore di vedere un po di codice
 
 

 Credo che l' utente voglia sapere quale sia il o i dati non validi
 
No. Sto dicendo che all'utente dici quello che non va in termini
adeguati al tipo di utente dell'applicazione. 
 
 
 
Credo che siamo arrivati al mio limite di gestione del post. O
cominciamo a tagliare le parti che giudichiamo non rilevanti o che non
ci interessano, oppure la cosa sta cominciando a diventare ingestibile.
Per lo meno, io non sono tanto capace di gestirla. Questo post sono
quasi 4500 parole... e' un piccolo articolo, troppo lungo per essere
seguito da chi non e' nella discussione!


 ancora avuto l' onore di vedere un po di codice
 
Ti sta per arrivare un post da 4500 parole... ho anche una vita! 
Ma poi non ho capito che codice vuoi. Se vuoi vedere del mio codice "in
generale" e' pieno il web, se vuoi codice specifico chiedi e ti sara'
dato.



Qualcosa ho tagliato