|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
[C#/Generics]Oggetti come parametri
Ciao a tutti
Ho un'interfaccia molto semplice così composta: Codice:
using System;
using Collection.Generics;
namespace UnderstandingGenerics
{
public interface IGenericTest<T>
{
void Add(T businessObject);
List<T> GetAll();
}
}
//una prima semplice implementazione:
public class Persona
{
private string nome;
private string cognome;
public Persona(string nome, string cognome)
{
this.nome = nome;
this.cognome = cognome;
}
}
public class Rubrica : IGenericTest<Persona>
{
public void Add(Persona persona)
{
//codice che aggiunge la persona alla rubrica
}
public List<Persona> GetAll()
{
//codice che ottiene tutte le persone presenti in rubrica
}
}
mi sono reso conto che così facendo sarò obbligato ad definire una classe che implementi i metodi dell'interfaccia per ogni tipo di entità che avrò. Non so se è un bene o un male... cmq, mi chiedevo se invece avessi una sola classe che implementa l'interfaccia, come potrei passarle dinamicamente gli oggetti che mi interessano ? Grazie mille RaouL.
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek
|
|
|
|
|
|
#2 |
|
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
Forse ho capito:
Codice:
public interface IProva
{
void Add(Object o);
}
public class Persona
{
private string nome;
public Persona(string nome)
{
this.nome = nome;
}
}
public class AzioniRubrica : IProva
{
public void Add(Object o)
{
//codice che aggiunge un oggetto alla rubrica
}
}
public class Rubrica
{
Persona persona;
Azienda azienda; //altra classe di prova
public void Test()
{
AzioniRubrica a = new AzioniRubrica();
persona = new Persona("raoul");
a.Add(persona);
a.Add(azienda);
}
}
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek
Ultima modifica di RaouL_BennetH : 29-04-2009 alle 14:38. |
|
|
|
|
|
#3 |
|
Senior Member
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
|
object no, non usarlo. E' proprio controproducente e costa troppo in termini di errori runtime e anche velocita' di esecuzione.
I generics sono stati introdotti apposta. Usare un'interfaccia qui, se non si hanno altri requisiti che ne vincolano l'esistenza, mi sembra un po' una stortura. Userei una classe, eventualmente astratta, per questo specifico problema. Codice:
namespace UnderstandingGenerics
{
public abstract class MyBaseCollection<T>
{
protected List<T> myList;
public MyBaseCollection()
{
myList=new List<T>();
}
public void Add(T businessObject)
{
myList.Add(businessObject);
}
public List<T> GetAll()
{
return myList;
}
// Metodo banale aggiunto perche' altrimenti verrebbe voglia di usare il
// generics List direttamente senza dover costruire
// questa classe astratta
public List<T> GetReverse()
{
return myList.Reverse().ToList();
}
}
}
//una prima semplice implementazione:
public class Persona
{
private string nome;
private string cognome;
public Persona(string nome, string cognome)
{
this.nome = nome;
this.cognome = cognome;
}
}
public class Rubrica : MyBaseCollection<Persona>
{
public Rubrica():base()
{
}
// Metodo per giustificare l'esistenza della classe Rubrica
// Altrimenti si sarebbe usata direttamente la generic MyBaseCollection
// (ovviamente non piu' Abstract in questo caso)
public Persona GetFirstByName(string name)
{
Persona ret = myList.
Where(per => per.nome==name).
FirstOrDefault();
return ret;
}
}
L'unico effetto sara' quello di impedire la costruzione di un oggetto della classe specifica MyBaseCollection, effetto magari voluto dall'applicazione. Un primo passo prima di costruire un nuovo Generics, e' quello di usarli. La maggior parte dei problemi si puo' risolvere usando Generics gia' fatti e classi normali. Per esigenze piu' complesse si puo' valutare la scrittura di una nuova classe Generic, cosa non proprio banale anche a causa della sintassi un po' forzata e poco leggibile. Una volta fatta pero', almeno, l'uso di un Generic ben fatto risulta essere semplice.
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto. E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test. |
|
|
|
|
|
#4 |
|
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
Ciao gugoXX
In linea di massima ho capito la tua spiegazione, e, con le seguenti premesse: 1) Sto usando le interfacce perchè sono il mio attuale argomento di studio 2) non è un'applicazione reale, solo didattica mi chiedevo però se: anzichè avere un'entità Rubrica, io avessi un'entità CalendarioAttività, se dovessi aggiungere più oggetti diversi fra di loro a CalendarioAttività, sarei costretto ad implementare i metodi dell'interfaccia o della classe astratta per ogni tipo di oggetto che devo aggiungere? cioè: Codice:
public interface IPlanning<T>
{
void Add(T oggetto);
}
public class Planning : IPlanning<Persona>
//già qui... mi blocco ...
//posso solo aggiungere un oggetto di tipo persona al mio planning,
//ma se dovessi aggiungere un appuntamento?
un'azienda ha dei dipendenti un'azienda ha dei fornitori quindi mi viene in mente un: azienda.Add(dipendente) e azienda.Add(fornitore) e di conseguenza penso che potrei avere un'unico gestore di queste azioni. Oppure è sbagliato proprio il mio approccio ? Grazie di cuore RaouL.
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek
|
|
|
|
|
|
#5 |
|
Senior Member
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
|
L'approccio non e' sbagliato, anche se non e' ortodosso.
Lo puo' diventare se devi giustificare le interfaccie per altri motivi. Ovviamente per questo esempio non solo non userei le interfaccie, ma come detto non userei neppure un nuova classe Generics, quanto piu' userei direttamente le Collection offerte dai namespace Generics. Comunque, per la tua implementazione ti e' sufficiente implementare l'interfaccia 2 volte... Codice:
public class Planning : IPlanning<Dipendente>, IPlanning<Fornitore> Il controsenso sta qui, dato che molto probaiblmente il codice per le 2 add sara' identico, e quindi il tuo legittimo dubbio se l'uso delle interfaccie sia proprio questo. Ebbene, ovviamente non lo e'. Raramente si inseriscono tecnicismi che forzano la riscrittura del codice, quanto piu' invece per ridurlo. Ti faccio un esempio concreto in cui l'uso delle interfaccie puo' essere utile. Immagina di avere un Database remoto a San Francisco, non da te manutenuto, che espone qualche Stored Procedure implementate su questo Database e che tu potrai chiamare. Potrebbe venire voglia semplicemente di costruire una classe Helper, con tanti metodi quante sono le stored procedure. Ti sono pero' arrivate delle direttive dall'alto, che ti consigliano fortemente di non legare l'implementazione pensando all'esistenza di un Database, perche' probabilmente in futuro l'azienda passera' ad usare WebService per l'accesso ai dati, che circonera' il Database che sara' di fatto reso invisibile, vietando quindi in futuro l'accesso diretto al database a qualsiasi utilizzatore aziendale. Verra' creato quindi da qualuno un WebService che esporra' tanti metodi quante erano le stored procedure che tu prima avresti potuto utilizzare. Pero' nel frattempo questo WebService non c'e'. E tu vorresti evitare di riscrivere tanto codice. Parti anche dal presupposto di avere una classe, come probabile che sia, che gia' sa come gestire connection string, aprire connessioni al database, eseguire query, lanciare stored procedure e ritirarne i risultati, etc. Uno dei possibili modi e' questo: - Crei l'interfaccia IRemoteSanFrancisco che contiene tanti metodi quante sono le stored procedure - Crei una classe RemoteDBSanFrancisco: DBGeneric, IRemoteSanFrancisco che deriva dalla tua DBGeneric e che sa quindi come lavorare sui Database, e che implementa anche l'interfaccia. - Ciascun metodo implementato dall'interfaccia sara' quindi la chiamata alla stored procedure relativa, ovviamente usando le facilities presenti nella classe base DBGeneric - Crei quindi anche la classe RemoteWSSanFrancisco: IRemoteSanFrancisco, ovvero la classe che nell'implementazione di ciascun metodo remoto andra' a chiamare il WebMethod relativo al WebService, invece che accedere direttamente al DB. Per concludere e mettere la ciliegina, doterai la tua soluzione di un SanFranciscoFactory, ovvero una classe con un Helper statico in grado di costruire un oggetto che implementa l'interfaccia IRemoteSanFrancisco Codice:
public static class SanFranciscoFactory
{
public static IRemoteSanFrancisco GetMyRemoteSanFrancisco()
{
// Qui si crea un'istanza della RemoteDBSanFrancisco
//oppure della RemoteWSSanFrancisco, a seconda di cosa c'e' scritto nei file di configurazione
// e se ne restituisce l'istanza
if (config qualcosa)
return new RemoteDBSanFrancisco();
else
return new RemoteWSSanFrancisco();
}
}
// L'utilizzatore quindi eseguira'
....
IRemoteSanFrancisco proxy = SanFranciscoFactory.GetMyRemoteSanFrancisco();
proxy.Metodo1(var1, var2, var3);
PippoClass inst = proxy.Metodo2(var4, var5);
...
Tutto il codice che "usa" i vai Metodo1, Metodo2, etc. non sara' da cambiare. PS: Questo discorso si puo' fare di nuovo con le abstract class invece che con le interfaccie. Farlo con le interfaccie pero' abilita' una metodologia avanzata di test che si chiama Mock, molto utile, direi indispensabile, se si usa il Test Driven Development.
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto. E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test. Ultima modifica di gugoXX : 29-04-2009 alle 16:56. |
|
|
|
|
|
#6 | ||
|
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
Quote:
Quote:
cioè: Codice:
public interface IAzienda
{
void CallProcedureAddCliente();
void CallProcedureAddPersona();
void CallProcedureAddFornitore();
......
}
public class DBManager : IAzienda
{
//metodi per le connessioni etc..
public void CallProcedureAddCliente()
{
blabla "insert into clienti..."
}
//etcetera..
}
//riducendo un pò l'ambito ad un umile winform:
//mi trovo sul form dei clienti
private buttonCliente_click(Object sender, EventArgs e)
{
DBManager db = new DBManager();
db.CallProcedureAddCliente();
}
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek
|
||
|
|
|
|
|
#7 | |
|
Senior Member
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
|
Quote:
Era quindi solo un esempio di uso delle interfaccie, senza generics di mezzo. Proponevo un metodo per ciascuna stored procedure. Se la stored procedure si chiama "UpdateUserState" Avrai una entry void UpdateUserState(int UserId, string UserState); Che verra' implementata dalla classe del Database chiamando la stored procedure e dalla classe del WebService chiamando il WebMethod.
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto. E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test. |
|
|
|
|
|
|
#8 | |
|
Senior Member
Iscritto dal: Oct 2005
Messaggi: 3306
|
Quote:
|
|
|
|
|
|
|
#9 | |
|
Senior Member
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
|
Quote:
Se poi (io) scrivessi interfacce invece che interfaccie sarebbe anche meglio.
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto. E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test. |
|
|
|
|
|
|
#10 | |
|
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
Quote:
Ma come sempre per ogni chiarimento nascono altri millemila dubbi Se ho ben compreso quindi, in presenza dell'interfaccia, se il DB di San Francisco ha 200 stored procedure, avrò duecento metodi da definire nell'interfaccia e, di conseguenza, da riscrivere nella classe che implementerà l'interfaccia? ...cerco di spiegare meglio il mio dubbio: Ogni volta che dovrò avere a che fare con il db, utilizzerò una sola classe che implementa l'interfaccia con tutti i suoi metodi ?
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek
|
|
|
|
|
|
|
#11 | ||
|
Senior Member
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
|
Quote:
Diciamo che ci sono modi ulteriori per semplificare la scrittura di codice in quei casi, ma l'idea sarebbe stata comunque quella di dover implementare 200 metodi in ciascuna delle 2 classi. Implementare come ora non impoterebbe piu' di tanto. Quote:
Che poi i metodi di questo servizio siano chiamate a database o chiamate a WebService, a chi questa classe la usa non importera' piu'.
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto. E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test. |
||
|
|
|
|
|
#12 |
|
Senior Member
Iscritto dal: Sep 2004
Messaggi: 3967
|
....rieccomi....
Altra piccolissima questione: lo scenario da te descritto sarebbe lo stesso in assenza di stored procedures lato Db ? per es.: Codice:
//codice interfaccia void InsertCliente(Cliente c) void InsertProdotti(Prodotto p) Grazie mille. RaouL.
__________________
Dai wafer di silicio nasce: LoHacker... il primo biscotto Geek
|
|
|
|
|
|
#13 | |
|
Senior Member
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
|
Quote:
Stored procedure o query dirette oppure LINQ to Sql, oppure Entity framework, etc. Tutti modi buoni per leggere dati dal database, e quindi per poter adottare la soluzione ad interfacce, ovvero se si prevede in futuro di utilizzare anche altri modi,oppure se si conta di testare con NMock che prevedono appunto l'uso di interfacce.
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto. E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test. |
|
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 20:59.



















