Poliformismo e Interfacce

venerdì 26 dicembre 2008 - 19.42

Teech Profilo | Expert

Ho un problema più logico che tecnico che a tutti gli effetti sembra banale:
stò facendo un programma che gestisce delle commesse. Ogni commessa è formata da più Componenti. Nel DB ho una tabella Componenti e per ogni record indico di che tipo di componente si tratta (Materiale, Lavorazione, Tempi macchina, ecc).
Il problema è che per ogni tipo vorrei crearmi una classe diversa: i campi del DB corrispondono ad alcune proprietà che vorrei implementare nelle mie classi.
Nel DB ho i seguenti campi:
- Codice Commessa
- Codice Componente
- Tipo Componente
- Quantità
- Costo
Per creare le mie classi e poter utilizzare poi il poliformismo (ogni istanza della classe "è un" Componente) vorrei crearmi delle interfacce. Il problema è nella gestione delle proprietà Quantità e Costo nelle interfacce:

Tutti i componenti hanno le proprietà Qantità e Costo, ma in base al tipo di componente queste proprietà possono essere modificate o meno:
- Se il componente è una lavorazione non possono essere modificati ne quantità (gestiti da un timetracker) ne costo (gestiti da un gestionale)
- Se il componente è materiale posso variare la quantità ma non il costo (gestito da un gestionale)
- Se il componente è una lavorazione esterna non posso variare la quantità (sempre =1 fra le altre cose) ma posso variare il costo
- ecc.. (altri scenari possibili)

A questo punto vado per creare la mia interfaccia IComponente ma non riesco a capire se metterci anche le Property Costo e Quantità: se le metto non posso gestire il fatto che la proprietà sia ReadOnly su ogni classe che implementa l'nterfaccia, mentre se non le metto nell'interfaccia devo "implementarle manualmente" su ogni classe...
Siccome non si tratta di espandere una classe (ereditarietà) ma che ogni Componente può avere diversi comportamenti, quale tecnica è corretto utilizzare? Per il poliformismo credo di averci preso, ma a livello di interfaccia come posso comportarmi?

Grazie!!!

P.S.: Dimenticavo un fattore che può essere importante: a volte la quantità può essere un TimeSpan e a volte un Double o un Integer... Questo mi ha fatto pensare al fatto di gestire delle interfacce che ereditano da IComponente per ogni tipo (ILavorazione, IMateriale, ecc...), ma ha senso secondo voi?
--------------
Maurizio Brini
--------------
Nessuna impresa è mai stata compiuta da un uomo ragionevole

freeteo Profilo | Guru

>Siccome non si tratta di espandere una classe (ereditarietà)
>ma che ogni Componente può avere diversi comportamenti, quale
>tecnica è corretto utilizzare? Per il poliformismo credo di averci
>preso, ma a livello di interfaccia come posso comportarmi?
io dichiararerei le proprietà nell'interfaccia come lettura/scrittura, e poi dove ti serve, nell'implementazione reale non fare niente nel set, quindi se qualcuno fa anche il set, di fatto viene ignorata come operazione.




>P.S.: Dimenticavo un fattore che può essere importante: a volte
>la quantità può essere un TimeSpan e a volte un Double o un Integer...
>Questo mi ha fatto pensare al fatto di gestire delle interfacce
>che ereditano da IComponente per ogni tipo (ILavorazione, IMateriale,
>ecc...), ma ha senso secondo voi?
mmmhh...questa situazione complica le cose.
L'interfaccia indica che l'oggetto avrà quelle caratteristiche, come vengano poi gestite realmente è demandato all'oggetto (che infatti potrebbe essere intercambiabile) se poi quella caratteristica è "completamente" un'altra cosa allora quello non ha senso essere messo nell'interfaccia, perchè l'oggetto non è più "astratto" o cmq "intercambiabile" diventa qualcos'altro.
In questi casi allora, dovresti avere diverse interfacce per le diverse tipologie, valutando quante e se hanno senso.

L'utilizzo di un'interfaccia è per poter avere codice generico, senza doversi tipizzare troppo perchè ti è comodo, ma devono essere oggetti simili, se hanno cose differenti, in quel caso devi tipizzarti per usare quell'oggetto specifico, perchè altrimenti il compilatore non può sapere che quell'oggetto implementa quell'interfaccia, ma che in quel caso specifico la proprietà è diversa da quella dell'interfaccia...perchè significa che non è più quell'interfaccia (un po' contorto...)

Altrimenti l'unica cosa è definirlo "object" e quidi vai via tranquillo perchè tutto eredita da object, facendoti i vari cast ogni volta che ti è necessario per usarlo...ma non la vedrei come una cosa tanto sensata, perchè perderesti la tipizzazione e potresti andare incontro ad errori a runtime...spesso sgradevoli.

Ciao.

Matteo Raumer
[MVP Visual C#]
http://blogs.dotnethell.it/freeteo

Teech Profilo | Expert

>mmmhh...questa situazione complica le cose.
>L'interfaccia indica che l'oggetto avrà quelle caratteristiche, come vengano poi gestite realmente è demandato all'oggetto (che infatti potrebbe essere >intercambiabile) se poi quella caratteristica è "completamente" un'altra cosa allora quello non ha senso essere messo nell'interfaccia, perchè l'oggetto non >è più "astratto" o cmq "intercambiabile" diventa qualcos'altro.
>In questi casi allora, dovresti avere diverse interfacce per le diverse tipologie, valutando quante e se hanno senso.
>
>L'utilizzo di un'interfaccia è per poter avere codice generico, senza doversi tipizzare troppo perchè ti è comodo, ma devono essere oggetti simili, se >hanno cose differenti, in quel caso devi tipizzarti per usare quell'oggetto specifico, perchè altrimenti il compilatore non può sapere che quell'oggetto >implementa quell'interfaccia, ma che in quel caso specifico la proprietà è diversa da quella dell'interfaccia...perchè significa che non è più >quell'interfaccia (un po' contorto...)
Quindi ho 3 strade percorribili (metto anche i Pro ed i Contro secondo delle mie supposizioni):
1) partendo dal fatto che il campo Quantità è originariamente un unico campo su un DB e quindi ha un dimensionamento solo (Float in SQL) potrei provare a riportare la proprietà Quantità per ogni classe a Double e gestire la visualizzazione/inserimento del dato sui vari form. (PRO/CONTRO: struttura del programma semplice ma gran sbattimento di gestione dei form e facilità di errore di formattazione)
2) Creare una interfaccia IComponente con le proprietà "fisse" (Codice, Commessa, ecc..) e delle interfacce che ereditano da IComponente, per poi "espandere" l'interfaccia di base
Public Interface ILavorazione Inherits IComponente ....

(PRO/CONTO: Gestione separata di ogni singola classe, quindi codice più prolisso, ma elasticità di utilizzo ampia. Possibile disallineamento della gestione delle proprietà comuni (quelle di IComponente)
3) Creo un'interfaccia IComponente con le proprietà comuni e la implemento in una classe astratta Componente. Eredito dalla classe astratta le varie classi ed espando la classe base con sole proprietà che mi interessano (PRO/CONTRO: codice abbastanza conciso ed elastico ma perdo la possibilità di usare le interfacce per le classi derivate -non avrebbe più senso usare le interfacce ILavorazione con le sole proprietà derivate senza sapere che eredita da IComponente-).

Se fosse un sondaggio, che busta sceglieresti (così, tanto per avere un orientamento di massima)?
Se ho scritto delle fesserie, ti prego correggimi!!!

A volte le fattispecie che sembrano più semplici creano insidie e la mia insicurezza nel programmare (ma soprattutto nell'analizzare) mi fanno pensare forse troppo. Grazie dell'aiuto!!!
--------------
Maurizio Brini
--------------
Nessuna impresa è mai stata compiuta da un uomo ragionevole

freeteo Profilo | Guru

>1) partendo dal fatto che il campo Quantità è originariamente
>un unico campo su un DB e quindi ha un dimensionamento solo (Float
>in SQL) potrei provare a riportare la proprietà Quantità per
non è del tutto assurdo, potrebbe anche essere una soluzione...tanto si tratterebbe di una sola differenza di controlli nelle forms...
Daltronde se stai salvando un datetime su un campo double del database, allora non è proprio fuori luogo come soluzione...



>2) Creare una interfaccia IComponente con le proprietà "fisse"
>(Codice, Commessa, ecc..) e delle interfacce che ereditano da
>IComponente, per poi "espandere" l'interfaccia di base
>Public Interface ILavorazione
> Inherits IComponente
> ....
questa soluzione mi piace di più, perchè ok che vai a fare diverse interfacce, ma almeno sai che tipi stai usando.
Rimane cmq il fatto che poi sul db si salva come numerico il che lo trovo 1po scomodo sinceramente...se gli oggetti hanno proprietà di tipo diverso con lo stesso nome, forse non sono molto "intercambiabili" io farei piuttosto interfacce separate.
Altra cosa che farei cmq, è pensare ad un'ereditarietà piuttosto che avere tante interfacce magari farle ereditare da un oggetto base.
Io uso tipicamente per "disaccoppiare" e quindi avvalermi di Factory per scegliere a runtime cosa realmente caricare ma a livello di Manager,DataProvider etc...insomma classi che svolgono delle funzioni, non a livello di Entità, per queste preferisco ereditarietà quando mi serve.



>3) Creo un'interfaccia IComponente con le proprietà comuni e
>la implemento in una classe astratta Componente. Eredito dalla
>classe astratta le varie classi ed espando la classe base con
>sole proprietà che mi interessano (PRO/CONTRO: codice abbastanza
ecco questa mi sembra la soluzione che tra le 3 sceglierei...facendo ereditare da "Componente" e via via magari fare altre classi comuni dove hai comportamenti comuni, tipo se la proprietà "Quantita" è in DateTime, fare una classe che eredita da quella e ridefinisce la proprietà come DateTime, e nel codice non usare la classe "Componente" ma magari "ComponenteDateTime" ossia un'altra classe che sia più tipizzata.

Ciao.

Matteo Raumer
[MVP Visual C#]
http://blogs.dotnethell.it/freeteo

Teech Profilo | Expert

Fatto!!!
Ho creato l'interfaccia ed implementata in una classe astratta. ho poi derivato dalla classe astratta tutti le classi "reali". Funziona bene, unico neo è che avendo una collection che popolo con varie istanze di queste classi, ma con tipi di classe astratta "perdo" le proprietà delle classi derivate in alcuni casi (ad esempio se imposto la collection come DataSource di un DataGridView, ma poco male).

Sempre nella collection ho implementato un metodo per popolare la collection stessa (prende i dati da un adapter che legge da un DB e istanzia le classi): mi sono visto costretto a usare delle costanti in questo modo:
Protected Overridable Sub Popola() Me.Items.Clear() For Each cp As IComponenteDB In _adapter.GetComponenti(_filtro) Select Case cp.Tipo Case "L" Me.Items.Add(New Lavorazione(CType(cp, IComponenteDB))) Case "M" Me.Items.Add(New Materiale(CType(cp, IComponenteDB))) Case ... End Select Next End Sub
Esiste un sistema più "dinamico" (non so se mi spiego) per effettuare questa scelta? (qui è solo una finitura minima proprio)

Grazie mille
--------------
Maurizio Brini
--------------
Nessuna impresa è mai stata compiuta da un uomo ragionevole

sanfra Profilo | Junior Member

Salve, siccome sto avendo un problema simile(spero di aver capito bene), non si potrebbe creare una classe astratta di tipo generico dove implemento tutti i metodi e due interfacce di cui una di tipo generico per la proprietà quantita e l'altra normale(con le proprietà costo), dove successivamente io faccio ereditare quest'ultima dall'interfaccia generica e poi dichiaro tre classi che ereditano sia dalla classe astratta che dall'interfaccia?

Non so se mi sono spiegato

Grazie

freeteo Profilo | Guru

Ciao e perdona il ritardo nella risposta, probabilmente avrai già risolto cmq ti rispondo lo stesso.

>che ereditano sia dalla classe astratta che dall'interfaccia?
qui sta la vera differenza, ossia l'interfaccia non si "estende" ma si "implementa" quindi diciamo che devi scriverti il codice tu dentro a quella proprietà/metodo per forza.

Se invece "estendi", te lo trovi già implementato dalla base, e se ti va bene non scrivi neinte, altrimenti fai un override (devi metterla virtual in questo caso) e scrivi tu il codice che vuoi per quel metodo/proprietà.

Ciao.

Matteo Raumer
[MVP Visual C#]
http://blogs.dotnethell.it/freeteo

sanfra Profilo | Junior Member

grazie per avermi risposto,Io,alla fine ho creato la classe astratta senza nessuna interfaccia ma con solo appunto una classe astratta generica cioè

public abstract class Componente<T>, dove all'interno dichiaro tutti i medoti di tipo!

e dove non ho bisogno di settare la proprietà (in questo caso quantità) gli creo un define dove gli dico appunto che se è questo compoenente mostrami la proprietà con Get e Set altrimenti solo mostrami solo la proprietà con solo Get

Non so se è performante quello che ho fatto,però ha funzionato ed ho usato il polimorfismo!

Tu cosa ne pensi?

Grazie ancora per il tuo iontervento!

freeteo Profilo | Guru

>Non so se è performante quello che ho fatto,però ha funzionato
>ed ho usato il polimorfismo!
>Tu cosa ne pensi?
oddio non ho capito esattamente cosa hai fatto, puoi fare un esempio di codice? anche un file allegato se vuoi con dentro la classe abstract e una derivata.Potrebbe essere valido anche il tuo metodo, magari se vedo nel dettaglio posso darti un parere più preciso.


>Grazie ancora per il tuo iontervento!
di niente, perdona il ritardo invece...

Grazie

Ciao.

Matteo Raumer
[MVP Visual C#]
http://blogs.dotnethell.it/freeteo
Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2024
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5