Newsgroup: it.comp.lang.visual-basic
Titolo: Annidamento classi VB.NET

Ciao a tutti, mi domandavo come "posizionare" delle classi.
Ho una classe poniamo CLIENTE, mi serve un metodo che mi restituisca un
oggetto cliente da un ID, che si chiamerà GetClienteById().
Ho visto che spesso viene creata una classe ClienteReader.
Ma come mi merita fare?
1)
Class Cliente
  property ...
  Shared Function GetClienteById()
End Class
2)
Class Cliente
  property ...
  Class ClienteReader
    Shared Function GetClienteById()
  End Class
End Class
3)
Class Cliente
  property ...
End Class
Class ClienteReader
  Shared Function GetClienteById()
End Class

 Ciao a tutti, mi domandavo come "posizionare" delle classi.
io uso come regola di base il "Single Responsibility Principle" e quindi 
"disegno" le classi (ad eccezione di qualche "utility") affinchè abbiano 
ognuna una singola "tipologia" di responsabilità/ruolo quindi, tra le 
tue "proposte", opterei per la "3"
Se dovessi farlo in un mio progetto, però, eviterei sicuramente di 
rendere shared le funzioni esposte dalla classe reader e molto 
probabilmente farei implementare a questa classe una interfaccia che ne 
rappresenti il tipo. Una cosa del genere, insomma:
Class Cliente
End Class
Interface IClienteMapper
	Function GetClienteById() As Cliente
End Interface
Class ClienteMapper
	Implements IClienteMapper
End Class
se stai usando il FX 3.5 o superiore, puoi anche optare (io lo faccio) 
pre trasformare quel "mapper" in un "Repository":
Interface IRepositoryCliente
	Inherits IQueryable(Of Cliente), IList(Of Cliente)
	Function GetClienteById() As Cliente
	Function GetClientiPerProvincia(nomeProvincia as String) As 
IQueryable(Of Cliente)
End Interface
aggiungendo all'interfaccia i metodi che ti fanno comodo, ma delegando 
l'implementazione ad un O/RM come L2SQL o Entity Framework
-- 
  .A
GUISA - 
http://www.guisa.org
UGIdotNET - 
http://www.ugidotnet.org
Read my blog at: 
http://blogs.ugidotnet.org/pape

Ciao,
io di solito, quando la classe e' strettamente legata ad un db, faccio una 
cosa ancora piu' automatica; assgnando un valore alla proprieta' ID 
dell'oggetto, questo si va automaticamente a caricare i propri dati tramite 
una query.
Per capirci
Public Class Cliente
    Public Property ID as Integer
        Get
           bla bla bla
       End Get
      Set (ByVal value as Integer)
           bla bla
           ReadData
      End Set
   End Property
End Class
Come vedi, nell'istruzione Set della proprieta' ID, viene chiamato il metodo 
ReadData, che tramite una query e un DataReader, va a prendere i dati dal DB 
e valorizza tutte le variabili locali associate.

 e valorizza tutte le variabili locali associate.
Beh nel mio caso la classe č si collegata ad un DB, ma non cosě
direttamente dipendente. Per questo volevo avere un "caricatore" esterno.

 rappresenti il tipo. Una cosa del genere, insomma:
Nel mio caso già la 3 mi pareva abbastanza spezzettata. Alla fine quella
classe avrà quel metodo e basta, e forse un altro, per quello avevo pensato
di metterlo come shared nella classe Cliente.
E per capirci anche la classe cliente (che non è cliente ma un'altra cosa),
ha 5/6 proprietà e nessun metodo per ora.
Per ora lascio la soluzione della doppia classe, ma mi fermo lì, vista la
banalità di concetto.
Ma il vantaggio di avere un'interfaccia invece che una shared?

 Ma il vantaggio di avere un'interfaccia invece che una shared?
in (estrema) sintesi, rendere "evidente" la volontà di disporre di 
diverse implementazioni dello stesso "tipo"; utile, ad esempio, se fai 
unit testing (diventa facile realizzare fake/mock da usare nei test).


 unit testing (diventa facile realizzare fake/mock da usare nei test).
L'ultima parte è un pelo arabo per me, ma grazie :)


il concetto è: se tu hai una classe "A" che usa una classe "B", allora 
"A dipende da B". Se provi a mettere "A" sul banco di test, quando "A" 
userà "B" sarà eventualmente soggetto ai fallimenti di B. Solo che tu 
stai osservando A, quindi per te è (erroneamente) A che ha fallito.
Se invece:
- A, invece che da B, dipendesse da IB (dove B Implements IB), all
- A non costruisse (Dim ... As New B) direttamente B, permettesse a 
"qualcuno" di "iniettare" la istanza di IB da utilizzare (Dpenedency 
Inversion Principle)
allora tu potresi istruire questo "qualcuno" ad iniettare nella 
applicazione "vera" una istanza di B, mentre nel codice dello unit test 
faresti iniettare in A una istanza di "FakeB" (dove FakeB Implements 
IB), che è una classe/oggetto che ha un comportamento "più o meno" 
cablato e quindi sicuro (es: non va davvero sul DB a leggere dei valori 
ma restituisce dei valori cablati). In questo modo, riesci a testare A 
mediante unit test in modo significavivamente "sicuro ed attendibile".
P.S.: tipicamente, quel "qualcuno" che inietta le istanze è un "IoC 
container", ossia (semplificando) una libreria che sa fare esattamente 
questa cosa (oltre ad interessanti altre) risparmiandoti lo sbattimento 
di implementarti questa funzionalità. Nel mondo .NET ormai ne esistono 
un bel po': Castle Windsor, StructureMap, NInject, Spring, Unity e, 
anche se è un po' particolare, anche MEF (che ha il vantaggio di essere 
"built-in" nel FX4)
