View Full Version : [Object Orientation] Metodi getter/setter. Che ne pensate?
Ciao a tutti. All'università sto seguendo il corso di Progettazione del software e ultimamente stavamo parlando di object orientation. Volevo sentire la vostra opinione sui metodi getter/setter. Li usate, preferite un altro metodo o trovate un compromesso? Se usate un altro metodo, quale? Grazie :)
Personalmente mi sono sempre sembrati una stronzata.
Io tendo a rendere tutto immutabile (ad esempio in java le mie variabili di istanza sono tutte "public final"... dove possibile ovviamente).
cdimauro
07-11-2011, 14:08
Preferisco le property, già dai tempi di Delphi.
Personalmente mi sono sempre sembrati una stronzata.
Io tendo a rendere tutto immutabile (ad esempio in java le mie variabili di istanza sono tutte "public final"... dove possibile ovviamente).
Anche secondo me sono una stronzata. Però per esempio, con il metodo che dici tu, se un tuo campo lo vuoi far diventare da int a long, hai qualche problema. O sbaglio? Mi riferisco al fatto che sia public.
Preferisco le property, già dai tempi di Delphi.
Cosa sono le property? Per esempio, in Java?
Anche secondo me sono una stronzata. Però per esempio, con il metodo che dici tu, se un tuo campo lo vuoi far diventare da int a long, hai qualche problema. O sbaglio? Mi riferisco al fatto che sia public.
Eh? Tipo? Scusa fammi un esempio di come non avresti lo stesso problema con getter/setter...
Eh? Tipo? Scusa fammi un esempio di come non avresti lo stesso problema con getter/setter...
No, appunto. Dico che anche quelli non sono la soluzione.
Kralizek
07-11-2011, 14:42
Preferisco le property, già dai tempi di Delphi.
vedo le property di delphi e rilancio con le automatic properties di c# 3.0
vedo le property di delphi e rilancio con le automatic properties di c# 3.0
Tu passi da doghe dei letti ad automatic properties di C# :p
Concettualmente sono una stronzata (sono le properties, ne più ne meno), ma in un linguaggio che non offre le properties tipo C++ o Java vanno usati, anche se a malincuore.
Ho provato a "farla semplice" e creare semplicemente membri pubblici, ma questo impedisce qualsiasi controllo su cosa viene settato dall'esterno e quando... e questo manco a dirlo è una fabbrica di bug.
Stesso dicasi per le letture, ad esempio qualcuno potrebbe decidere di prendersi un puntatore al membro, che poi viene invalidato senza che lui lo sappia.
Quindi, in C++/Java, getter e setter vanno usati a forza. In altri linguaggi, fate vobis :D
PS: mentre in C++ sono spesso inline e quindi non impattano sulle performance, in Java sono vere chiamate a funzione... e quindi oltre al danno, la lentezza!
banryu79
07-11-2011, 15:12
PS: mentre in C++ sono spesso inline e quindi non impattano sulle performance, in Java sono vere chiamate a funzione... e quindi oltre al danno, la lentezza!
Hai mai provato a vedere il bytecode generato?
No perchè a sentir voi, pare sempre che negli altri linguaggi il compilatore sia furbo mentra javac deve essere per forza una ciabatta che non è capace a ottimizzare roba del genere...
Kralizek
07-11-2011, 15:24
Tu passi da doghe dei letti ad automatic properties di C# :p
OT: lol, come ti chiami su tgm? :O
Risposta in privato :O [/OT]
Hai mai provato a vedere il bytecode generato?
No perchè a sentir voi, pare sempre che negli altri linguaggi il compilatore sia furbo mentra javac deve essere per forza una ciabatta che non è capace a ottimizzare roba del genere...
A voi!
// H4xx0r.java
class H4xx0r {
private int leet;
public int getLeet() { return leet; }
public void setLeet(int leet) { this.leet = leet; }
public static void main(String... args) {
H4xx0r h = new H4xx0r();
h.setLeet(1337);
System.out.println(h.getLeet());
}
}
// H4xx0r2.java
class H4xx0r2 {
public int leet;
public static void main(String... args) {
H4xx0r2 h = new H4xx0r2();
h.leet = 1337;
System.out.println(h.leet);
}
}
// javap -c H4xx0r
Compiled from "H4xx0r.java"
class H4xx0r extends java.lang.Object{
H4xx0r();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public int getLeet();
Code:
0: aload_0
1: getfield #2; //Field leet:I
4: ireturn
public void setLeet(int);
Code:
0: aload_0
1: iload_1
2: putfield #2; //Field leet:I
5: return
public static void main(java.lang.String[]);
Code:
0: new #3; //class H4xx0r
3: dup
4: invokespecial #4; //Method "<init>":()V
7: astore_1
8: aload_1
9: sipush 1337
12: invokevirtual #5; //Method setLeet:(I)V
15: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
18: aload_1
19: invokevirtual #7; //Method getLeet:()I
22: invokevirtual #8; //Method java/io/PrintStream.println:(I)V
25: return
}
// javap -c H4xx0r2.java
Compiled from "H4xx0r2.java"
class H4xx0r2 extends java.lang.Object{
public int leet;
H4xx0r2();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class H4xx0r2
3: dup
4: invokespecial #3; //Method "<init>":()V
7: astore_1
8: aload_1
9: sipush 1337
12: putfield #4; //Field leet:I
15: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
18: aload_1
19: getfield #4; //Field leet:I
22: invokevirtual #6; //Method java/io/PrintStream.println:(I)V
25: return
}
banryu79
07-11-2011, 16:00
A voi!
snip...
Ok, è una ciabatta :asd:
Scherzi (e svarione mio) a parte, il costo a runtime dei getter/setter è trascurabile, se vengono effettuate poche chiamate.
Se vengono effettuate molte chiamate, la JVM compila il metodo in maniera nativa (normalmente con HotSpot ricordo che se un metodo viene invocato 1000 volte a runtime viene compilato).
Non saprei se al posto della compilazione nativa di un getter/setter liscio sia prevista la possibilità di effettuare un accesso diretto al campo...
Chiaro che se la classe è final e i suoi campi sono immutabili, questi utlimi possono semplicemente essere esposti come pubblici.
In ogni caso, a livello prestazionale, quanto può mai essere rilevante il peso dei getter/setter nella tipica applicazione media per cui viene usato Java?
In ogni caso, a livello prestazionale, quanto può mai essere rilevante il peso dei getter/setter nella tipica applicazione media per cui viene usato Java?
Dato che ho qualche minuto di inattività...
// Setters.java
class Setters {
private double x;
public void setX(double x) { this.x = x; }
public static void main(String... args) {
for (int i = 1; i <= 5; ++i) {
System.out.print("Try " + i + ": ");
final Setters victim = new Setters();
final long start = System.nanoTime();
for (int j = 0; j < 100000; ++j) {
victim.setX(Math.random());
}
System.out.println((System.nanoTime() - start) / 1000000000.0);
}
}
}
// NoSetters.java
class NoSetters {
public double x;
public static void main(String... args) {
for (int i = 1; i <= 5; ++i) {
System.out.print("Try " + i + ": ");
final NoSetters victim = new NoSetters();
final long start = System.nanoTime();
for (int j = 0; j < 100000; ++j) {
victim.x = Math.random();
}
System.out.println((System.nanoTime() - start) / 1000000000.0);
}
}
}
I risultati sono "meh"...
c:\> java Setters
Try 1: 0.009697214
Try 2: 0.00675697
Try 3: 0.007349395
Try 4: 0.006881777
Try 5: 0.007874961
c:\> java NoSetters
Try 1: 0.014335313
Try 2: 0.006779257
Try 3: 0.006854627
Try 4: 0.007369251
Try 5: 0.006836393
cdimauro
07-11-2011, 17:49
Cosa sono le property? Per esempio, in Java?
Java non le ha ovviamente, per cui nisba esempi. :D
Ti evito Delphi e fornisco un esempio (http://msdn.microsoft.com/en-us/library/x9fsa0sw(v=vs.80).aspx) semplice in C# direttamente dalla mamma.
vedo le property di delphi e rilancio con le automatic properties di c# 3.0
Beh, io ho detto già ai tempi. Come dire: roba vecchia.
Poi sappiamo pure chi è il papà di C#, no? :cool:
Ovviamente son d'accordo con l'evoluzione che hanno preso in C# (ci sono pure le property virtuali :D).
Quindi, in C++/Java, getter e setter vanno usati a forza. In altri linguaggi, fate vobis :D
C'è di meglio, appunto. :cool:
PS: mentre in C++ sono spesso inline e quindi non impattano sulle performance, in Java sono vere chiamate a funzione... e quindi oltre al danno, la lentezza!
Questo è anche dovuto al fatto che normalmente i metodi in Java sono virtuali, per cui il compilatore non può certo renderli inline come fa il C++, dove sei TU a dichiarare virtuali i metodi se proprio li vuoi così, altrimenti sono assimilabili a delle normali funzioni e quindi il compilatore può rovistarci dentro per capire se può ottimizzare qualcosa. ;)
@banryu79: lol! A difendere la VM Java si rischia sempre un sacco :D
@shinya: quel test fa acqua, random è troppo più pesante di una singola chiamata a funzione ;)
Prova a fare l'unroll e usare una roba del genere
for (int i = 1; i <= 5; ++i) {
System.out.print("Try " + i + ": ");
final Setters victim = new Setters();
final long start = System.nanoTime();
int lol = 2389472;
for (int j = 0; j < 100000; ++j) {
victim.setX(lol);
victim.setX(lol);
victim.setX(lol);
victim.setX(lol);
victim.setX(lol);
victim.setX(lol);
victim.setX(lol);
victim.setX(lol);
}
System.out.println((System.nanoTime() - start) / 1000000000.0);
}
vedrai che la differenza si vede!
Comunque, il peso del getter e setter è sicuramente poco, ma in codici compute-intensive è sicuramente meglio non averlo ;)
@cdimauro il NON poter dichiarare qualcosa "non virtuale" invece, prova che Java è un pò una fetecchia...
ma d'altra parte grazie alla mancanza di operator overloading pure le somme tra vettori devono essere una chiamata a funzione virtuale, quindi sta messo male :asd:
cdimauro
07-11-2011, 18:44
Quello sì, l'overloading è una manna dal cielo (per anni ne ho sentito la mancanza in Delphi).
Però le funzioni non virtuali le puoi dichiarare come static: sono la stessa cosa alla fine. ;)
@Tommo non mi pare che banryu abbia detto castronate
cmq ho provato il tuo codice alzando a 1 miliardo il numero di iterazioni del for: con i setter ho avuto 0.03526231 secondi di media dei 5 try, mentre senza setter ho avuto 0.03517135.
Quindi, su 1 miliardo di iterazioni, il codice coi setter è più lento dello 0,0026% rispetto al codice senza setter, ovvero 91microsecondi (sempre su 1 miliardo di iterazioni).
E' palese che non posso spalmare 91microsecondi su 8 miliardi di set, ciò mi fa dedurre che probabilmente la jvm ottimizzi "strada facendo" e che comunque non ci sia un calo di prestazioni degno di nota. Se mi importasse quel margine di performance, allora farei meglio a passare al C o direttamente all'assembly...
Conclusione: non mi pare si possa dire qualcosa su quanto schifo faccia il java sulla base di questi discorsi. Poi si sa, è solo una questione di simpatia o antipatia personale verso i linguaggi.
Kralizek
07-11-2011, 20:17
@Tommo non mi pare che banryu abbia detto castronate
cmq ho provato il tuo codice alzando a 1 miliardo il numero di iterazioni del for: con i setter ho avuto 0.03526231 secondi di media dei 5 try, mentre senza setter ho avuto 0.03517135.
Quindi, su 1 miliardo di iterazioni, il codice coi setter è più lento dello 0,0026% rispetto al codice senza setter, ovvero 91microsecondi (sempre su 1 miliardo di iterazioni).
E' palese che non posso spalmare 91microsecondi su 8 miliardi di set, ciò mi fa dedurre che probabilmente la jvm ottimizzi "strada facendo" e che comunque non ci sia un calo di prestazioni degno di nota. Se mi importasse quel margine di performance, allora farei meglio a passare al C o direttamente all'assembly...
Conclusione: non mi pare si possa dire qualcosa su quanto schifo faccia il java sulla base di questi discorsi. Poi si sa, è solo una questione di simpatia o antipatia personale verso i linguaggi.
beh oddio... arrivati alla versione 7 potrebbero anche provare a darvi un po' di zucchero sintattico, non fa ingrassare mica...
public class MyClass
{
public int Id { get; set; }
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
}
@Tommo non mi pare che banryu abbia detto castronate
cmq ho provato il tuo codice alzando a 1 miliardo il numero di iterazioni del for: con i setter ho avuto 0.03526231 secondi di media dei 5 try, mentre senza setter ho avuto 0.03517135.
Quindi, su 1 miliardo di iterazioni, il codice coi setter è più lento dello 0,0026% rispetto al codice senza setter, ovvero 91microsecondi (sempre su 1 miliardo di iterazioni).
E' palese che non posso spalmare 91microsecondi su 8 miliardi di set, ciò mi fa dedurre che probabilmente la jvm ottimizzi "strada facendo" e che comunque non ci sia un calo di prestazioni degno di nota. Se mi importasse quel margine di performance, allora farei meglio a passare al C o direttamente all'assembly...
Conclusione: non mi pare si possa dire qualcosa su quanto schifo faccia il java sulla base di questi discorsi. Poi si sa, è solo una questione di simpatia o antipatia personale verso i linguaggi.
Strano, tempo fa mi capitò proprio di ottimizzare un ciclo rimuovendo i getters, avranno migliorato la JVM :D
Cmq si, l'antipatia verso Java sussiste :asd:
Concettualmente sono una stronzata (sono le properties, ne più ne meno), ma in un linguaggio che non offre le properties tipo C++ o Java vanno usati, anche se a malincuore.
Su su, non esiste cosa che non si possa fare in modo macchinoso, convoluto e contorto anche in C++.
Basta una spruzzata di template, un pizzico di lambda, un paio di operatori di conversione e il gioco e' fatto !
fa il C++ tutto quel che vuoi tu, bibidi bobidi bu! :D
#include <iostream>
#include <functional>
using namespace std;
template<typename T>
struct Property
{
typedef std::function<int()> Getter;
typedef std::function<void(int)> Setter;
Getter getter;
Setter setter;
Property(const Getter& g, const Setter& s):getter(g),setter(s){}
operator T(){ return getter(); }
Property& operator=(const T& x){ setter(x); return *this; }
};
struct FooBar
{
int x;
Property<int> property;
FooBar():property([this](){ return x; },
[this](int y){ x =y; })
{
}
};
Quasi come la versione C# :asd:, pero' funziona (o dovrebbe, ho fatto solo un paio di prove...)
@shinya: quel test fa acqua, random è troppo più pesante di una singola chiamata a funzione ;)
Sarà che sono ancora addormentato... ma non ho mica capito la tua obiezione. Ho usato random() per usare una funzione a caso (scusate...) in modo da essere sicuro che il ciclo facesse effettivamente qualcosa. Se una funzione impiega un tempo T lo fa sia se passi il risultato ad un setter sia se fai un'assegnazione ad una variabile, quindi mi sfugge perché dovrebbe falsare qualcosa.
Nel tuo codice invece assegni un valore costante N volte... è probabile che hotspot alla lunga lo capisca e dopo un po' traduca quel codice in un bel nop.
Così a naso eh...
ps. cmq il punto è che non c'è 'sta gran differenza tra assegnazione e setter a livello di prestazioni.
fa il C++ tutto quel che vuoi tu, bibidi bobidi bu! :D
Spettacolare! :tapiro:
Kralizek
08-11-2011, 08:33
Su su, non esiste cosa che non si possa fare in modo macchinoso, convoluto e contorto anche in C++.
Basta una spruzzata di template, un pizzico di lambda, un paio di operatori di conversione e il gioco e' fatto !
fa il C++ tutto quel che vuoi tu, bibidi bobidi bu! :D
#include <iostream>
#include <functional>
using namespace std;
template<typename T>
struct Property
{
typedef std::function<int()> Getter;
typedef std::function<void(int)> Setter;
Getter getter;
Setter setter;
Property(const Getter& g, const Setter& s):getter(g),setter(s){}
operator T(){ return getter(); }
Property& operator=(const T& x){ setter(x); return *this; }
};
struct FooBar
{
int x;
Property<int> property;
FooBar():property([this](){ return x; },
[this](int y){ x =y; })
{
}
};
Quasi come la versione C# :asd:, pero' funziona (o dovrebbe, ho fatto solo un paio di prove...)
:O
ora peró usala sta property! so proprio curioso di vedere come potrebbe funzionare :D
@marco.r brrrrrrr :D ste cose mi mettono ansia solo a pensare ai bug che ti escono :asd: certo definire le proprietá nell'initializer list è brutto forte. Ma è C++11?
@shinya mettiamo che una chiamata a random causi l'equivalente di 9 chiamate a funzione... A quel punto la chiamata aggiuntiva del setter è solo 1/10 del tempo totale, quindi la differenza dopo aver ottimizzato quel decimo, se c'è, è molto meno evidente.
Trattasi della legge di Ahmdal :D
Comunque si, a quanto pare non c'è comunque differenza... Mistero!
Comunque non ne facevo un fatto di prestazioni ma di design ad oggetti :stordita:
banryu79
08-11-2011, 10:36
Comunque non ne facevo un fatto di prestazioni ma di design ad oggetti :stordita:
Leggendo il tuo primo post, immaginavo :)
All'università sto seguendo il corso di Progettazione del software e ultimamente stavamo parlando di object orientation. Volevo sentire la vostra opinione sui metodi getter/setter. Li usate, preferite un altro metodo o trovate un compromesso? Se usate un altro metodo, quale?
Per parlare di un eventuale ruolo di una qualsiasi cosa nella teoria dell'orientamento agli oggetti bisogna prima chiarire precisamente cosa si intede per orientamento agli oggetti.
Cos'è un "oggetto" nella teoria dell'orientamento agli oggetti?
C'è un vecchio post di PGI-Bis al riguardo, se lo trovo ti pubblico qua il link.
Letto quello e condiviso (cosa non scontata) sono convinto che capirai meglio la tua domanda e farai una scelta.
@EDIT: ecco il thread citato:
http://www.hwupgrade.it/forum/showthread.php?t=2043852 (la ciccia inizia dal post #7)
anche in quest'altro thread si trovano alcune riflessioni interessanti:
http://www.hwupgrade.it/forum/showthread.php?t=2199058
Strano, tempo fa mi capitò proprio di ottimizzare un ciclo rimuovendo i getters, avranno migliorato la JVM :D
Cmq si, l'antipatia verso Java sussiste :asd:
Mah probabilmente sono ottimizzazioni alla JVM fatte nel tempo, concorderei nel dire che qualche anno fa java potesse essere un linguaggio da usare solo per applicazioni poco impegnate. Però insomma, è probabilmente il linguaggio più diffuso al mondo dopo la coppia C/C++, non penso che faccia così schifo... sicuramente ci sono linguaggi stilisticamente e semanticamente migliori e più prestanti, ma vabbè... i linguaggi di programmazione sono sempre in evoluzione.
IN OGNI CASO: ho perso (non solo io penso) il filo del discorso... di cosa si sta discutendo di preciso?
Cosa potrebbero offrire in java di preciso le properties in più rispetto a una coppia di metodi set/get o un campo pubblico accessibile direttamente?
:O
ora peró usala sta property! so proprio curioso di vedere come potrebbe funzionare :D
Ah la usi quasi come un qualsiasi attributo dell'oggetto.
Puoi provare qualcosa tipo
int main()
{
FooBar foo;
foo.property = 10;
std::cout << foo.property << endl;
}
O meglio ancora cambia il getter e il setter in
FooBar():property([this](){ return 2*x; },
[this](int y){ x =y/2; })
int main()
{
FooBar foo;
foo.property = 10;
std::cout << foo.x << ' ' << foo.property << endl;
}
Dovrebbe stamparti 5 e 10 alla console
Il trucco sta nello sfruttare la conversione automatica da int a Property<int> quando assegno e da Property<int> a int quando leggo. Questo vuol dire che tra le altre cose mi perdo la conversione automatica. Non ho sottomano un compilatore aggiornato, per cui non posso provare ora, ma se provi ad assegnare un float dovrebbe darti errore
@marco.r brrrrrrr :D ste cose mi mettono ansia solo a pensare ai bug che ti escono :asd: certo definire le proprietá nell'initializer list è brutto forte. Ma è C++11?
Ci sono un paio di cose da valutare (tipo, ritorno per valore o per riferimento), ma grandi bug non te ne escono. Eventuali errori se lo usi estensivamente vengono fuori presto.
Comunque la parte c++11 e' solo quella relativa alla lambda.
Volendo uno puo' usare c++03 usando puntatori a membro:
#include <iostream>
#include <functional>
#include <boost/bind.hpp>
using namespace std;
template<typename T,typename Owner,T (Owner::*Getter)(), void (Owner::*Setter)(const T&)>
struct Property
{
Owner* owner;
Property(Owner* o):owner(o) { }
operator T(){ return boost::bind( Getter, owner )(); }
Property& operator=(const T& x){ boost::bind( Setter, owner, _1)(x); return *this; }
};
#define PROPERTY(value,klass,getter,setter) Property<value, klass,&klass::getter,&klass::setter>
struct FooBar
{
int x;
int getX();
void setX(const int&);
Property<int,FooBar,&FooBar::getX,&FooBar::setX> property;
FooBar():property(this),property2(this)
{
}
};
int FooBar::getX()
{
return x*2;
}
void FooBar::setX(const int& y)
{
x = y/2;
}
int main()
{
FooBar foo;
// foo.property = 10;
foo.property2 = 10;
cout << foo.x << ' ' << foo.property << endl;
}
Rispetto alla prima versione ha pregi e difetti
difetti: La notazione e' piu' pesante perche' si deve ripetere il nome della classe tre volte inutilmente, inoltre devi scriverti dei metodi a parte, inoltre se vuoi poter chiamare metodi privati devi mettere la property come friend (o comunque usare accorgimenti analoghi).
pregi: dovrebbe essere piu' performante (perche' i metodi chiamati sono noti a tempo di compilazione... ma non e' detto), non definisci la chiamata nel costruttore, ma soprattutto se setX e getX sono virtual puoi ridefinire le proprieta' nelle classi derivate.
ESSE-EFFE
08-11-2011, 13:36
Volevo sentire la vostra opinione sui metodi getter/setter. Li usate, preferite un altro metodo o trovate un compromesso? Se usate un altro metodo, quale? Grazie :)
Dai un'occhiata qui:
http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html
http://www.javaworld.com/javaworld/jw-01-2004/jw-0102-toolbox.html
(parla di Java, ma molti concetti sono generici)
Personalmente sono abbastanza d'accordo con Holub. Se è possibile evitare mutators e accessors (che siano metodi o proprietà poco cambia) li evito (e quasi sempre è possibile).
tomminno
08-11-2011, 14:19
Dai un'occhiata qui:
http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html
http://www.javaworld.com/javaworld/jw-01-2004/jw-0102-toolbox.html
(parla di Java, ma molti concetti sono generici)
Personalmente sono abbastanza d'accordo con Holub. Se è possibile evitare mutators e accessors (che siano metodi o proprietà poco cambia) li evito (e quasi sempre è possibile).
Bah insomma, senza get e set i POJO non esisterebbero, e non c'è niente di meglio che mettere in un POJO quanto letto da db o un webservice.
Poi come la mettiamo con le configurazioni? Tutte nel costruttore?
E senza i getter come fai a comunicare qualcosa all'esterno?
Mai visto un programma fatto di classi che non comunicano tra di loro in nessun modo.
ESSE-EFFE
08-11-2011, 14:32
Poi come la mettiamo con le configurazioni? Tutte nel costruttore?
E perchè mai?
E senza i getter come fai a comunicare qualcosa all'esterno?
Mmm... mi sa che non hai letto bene bene tutto...
Mai visto un programma fatto di classi che non comunicano tra di loro in nessun modo.
E lo credo (dove l'hai letto poi?), ma il punto non è quello, casomai si discute sul "come" comunicare e mi viene in mente il principio OOP "Tell, don't ask".
Leggendo il tuo primo post, immaginavo :)
Per parlare di un eventuale ruolo di una qualsiasi cosa nella teoria dell'orientamento agli oggetti bisogna prima chiarire precisamente cosa si intede per orientamento agli oggetti.
Cos'è un "oggetto" nella teoria dell'orientamento agli oggetti?
C'è un vecchio post di PGI-Bis al riguardo, se lo trovo ti pubblico qua il link.
Letto quello e condiviso (cosa non scontata) sono convinto che capirai meglio la tua domanda e farai una scelta.
@EDIT: ecco il thread citato:
http://www.hwupgrade.it/forum/showthread.php?t=2043852 (la ciccia inizia dal post #7)
anche in quest'altro thread si trovano alcune riflessioni interessanti:
http://www.hwupgrade.it/forum/showthread.php?t=2199058
Ti ringrazio, però non so se ho bisogno di questo dettaglio sulla teoria. A me piacerebbe capire come creare sistemi ad oggetti ben progettati, flessibili ai cambiamenti. Non so se mi servono solo i design patterns o che. Però nel frattempo mi chiedevo questa cosa sui getter/setter :stordita:
Dai un'occhiata qui:
http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html
http://www.javaworld.com/javaworld/jw-01-2004/jw-0102-toolbox.html
(parla di Java, ma molti concetti sono generici)
Personalmente sono abbastanza d'accordo con Holub. Se è possibile evitare mutators e accessors (che siano metodi o proprietà poco cambia) li evito (e quasi sempre è possibile).
Sono proprio quei link che mi hanno portato qui in realtà :D
A me piacerebbe capire come creare sistemi ad oggetti ben progettati, flessibili ai cambiamenti.
Questo non lo sa fare nessuno. :fagiano:
tomminno
09-11-2011, 07:56
E lo credo (dove l'hai letto poi?), ma il punto non è quello, casomai si discute sul "come" comunicare e mi viene in mente il principio OOP "Tell, don't ask".
"Tell" significa comunicare con eventi, "Ask" significa chiamare un metodo.
Ora dimmi te anche con Hibernate come faresti ad usare il principio del Tell. Cos'è Hibernate dovrebbe eseguire query a caso e comunicarle?
Oppure c'è qualcuno che gli richiede qualcosa di preciso?
Ci sono poi tanti discorsi che non tornarno ad esempio se un tipo deve cambiare da int a long. Per queste cose è stato inventato il refactoring.
Se il mio dominio applicativo è cambiato da necessitare una modifica sul tipo di una variabile, vorrei vedere come dovrebbe essere fatto e infatti a riguardo non c'è nessuna spiegazione. Cos'è usiamo tutti object così siamo sicuri che vada sempre bene?
Secondo l'articolo bisogna usare interfacce. Bene ma una interfaccia esporrà o tutti object oppure, se espone qualcosa di sensato, esporrà int e io dovrò modificarlo in long.
In sostanza non c'è modo di scrivere qualcosa di sensato che continui a compilare nel caso di cambiamento tra int e long.
[Edit]
Dimenticavo: tutta questa puzza su come getter e setter siano il male e poi basta rinominarli in add e provide perchè invece siano il bene? Gli esempi di codice forniti nel secondo articolo riportano sempre l'uso di getter e setter (il male) sotto mentite spoglie...
ESSE-EFFE
09-11-2011, 09:42
"Tell" significa comunicare con eventi, "Ask" significa chiamare un metodo.
Risposta breve: no. Ma il punto non è tanto quello, più che altro si parte dal data hiding e si arriva a quel concetto.
Se il mio dominio applicativo è cambiato da necessitare una modifica sul tipo di una variabile, vorrei vedere come dovrebbe essere fatto e infatti a riguardo non c'è nessuna spiegazione.
Ma come no! C'è un pezzetto di codice semplice semplice proprio come esempio per questo problema!
Bene ma una interfaccia esporrà o tutti object oppure, se espone qualcosa di sensato, esporrà int e io dovrò modificarlo in long.
Ma perchè deve esporre il dato e quindi implicitamente il suo tipo?
In sostanza non c'è modo di scrivere qualcosa di sensato che continui a compilare nel caso di cambiamento tra int e long.
Non sono d'accordo. La classe che usa quel dato ovvio che va adattata (refactoring), ma le classi che dipendono da essa non necessariamente. Certo, questo solo se non esponi il dato stesso.
tomminno
09-11-2011, 10:52
Ma come no! C'è un pezzetto di codice semplice semplice proprio come esempio per questo problema!
Se ti riferisci all'esempio del Money mi pare proprio che non risolva niente, dato che nell'esempio rimane un piccolissimo problema: non hai modo di sapere qual è il totale.
Per saperlo devi avere per forza un metodo get.
Pensa un pò se dovessi mostrare all'utente un grafico con l'andamento giornaliero/mensile/annuale.
Senza quel banalissimo metodo get cosa me ne faccio dell'oggetto Money???
Come posso estrarre l'informazione interna alla classe?
Money deve sapere come stamparsi su qualunque tipo di output?
Quindi Money dovrebbe avere una qualche dipendenza chessò dalla interfaccia per i log e anche da qualche libreria grafica ad esempio per renderizzarsi su una immagine e magari pure da qualche orm/libreria di accesso ai dati nel momento in cui io volessi salvare il suo stato su db.
Money deve implementare al suo interno tutte le implementazioni di ogni possibile utilizzo e questo procedimento dovrebbe essere replicato per ogni classe.
Nel momento in cui io avessi necessità di utilizzare Money in un nuovo contesto devo modificare la classe, magari aggiungendo dipendenze.
Ma se questo Money fa parte di una libreria condivisa???
Pensa un pò se io dovessi usare il totale della classe Money e inserirlo all'interno di una chiamata soap per comunicarlo a terze parti.
Money non potrebbe essere a conoscenza degli altri campi necessari alla valorizzazione della richiesta soap perchè esulano dal proprio contesto.
Come farei senza un cavolo di metodo get?
Che poi il secondo articolo si autocontraddice in quanto utilizza getter e setter a piene mani nell'esempio di pagina 4 (http://www.javaworld.com/javaworld/jw-01-2004/jw-0102-toolbox.html?page=4).
Non è che cambiado in nomi da get/set ad add/provide cambi qualcosa eh!
Ma perchè deve esporre il dato e quindi implicitamente il suo tipo?
Vedi sopra.
Non sono d'accordo. La classe che usa quel dato ovvio che va adattata (refactoring), ma le classi che dipendono da essa non necessariamente. Certo, questo solo se non esponi il dato stesso.
Se non esponi quel dato devi adattare l'implementazione interna ad ogni possibile utilizzo esterno, mi sembra che sia pure peggio...
ESSE-EFFE
09-11-2011, 12:51
Se ti riferisci all'esempio del Money mi pare proprio che non risolva niente, dato che nell'esempio rimane un piccolissimo problema: non hai modo di sapere qual è il totale.
Lo sa l'oggetto qual è il totale. Se servono operazioni su quel totale, devi dirlo all'oggetto di eseguirle, non chiedere il totale ed eseguire le operazioni esternamente. Questo è il concetto espresso.
Senza quel banalissimo metodo get cosa me ne faccio dell'oggetto Money???
Eh ma allora l'oggetto Money cosa fa? E' solo un contenitore di dati? E a che servirebbe?
Money deve implementare al suo interno tutte le implementazioni
No, le interfacce servono a quello.
Se non esponi quel dato devi adattare l'implementazione interna ad ogni possibile utilizzo esterno, mi sembra che sia pure peggio...
Come viene chiaramente detto negli articoli, e poi io mi fermo, il design si basa su una serie di scelte. Non c'è scritto di non usare mai get o set. Secondo il suo parere, ma anche quello di altri "illustri", laddove si possa evitare, andrebbe evitato. Viene detto che quasi sempre questo è possibile e per esperienza posso dire di essere d'accordo. Il tutto naturalmente ragionando in ottica OOD, altrimenti rendo tutti i membri public che poco cambia dall'avere una serie di get/set composti da una sola riga (anche questo viene accennato nell'articolo).
E cito:
"Since accessors violate the encapsulation principle, you can reasonably argue that a system that heavily or inappropriately uses accessors simply isn't object oriented. If you go through a design process, as opposed to just coding, you'll find hardly any accessors in your program"
tomminno
09-11-2011, 15:27
Lo sa l'oggetto qual è il totale. Se servono operazioni su quel totale, devi dirlo all'oggetto di eseguirle, non chiedere il totale ed eseguire le operazioni esternamente. Questo è il concetto espresso.
La classe deve gestire le logiche per calcolare il totale, ma io ne ho poi bisogno all'esterno per gli usi più disparati.
Eh ma allora l'oggetto Money cosa fa? E' solo un contenitore di dati? E a che servirebbe?
Serve a contenere le logiche di calcolo del totale.
Ma se non gli metto un get per ottenere il totale che me ne faccio?
No, le interfacce servono a quello.
Interfacce che non possono esporre get e set perchè sono il male.
L'informazione sul totale non potrà mai uscire dalla classe Money!
Money non potrà mai passarla a qualche altra classe perchè significherebbe chiamare un metodo set!
Secondo il suo parere, ma anche quello di altri "illustri", laddove si possa evitare, andrebbe evitato. Viene detto che quasi sempre questo è possibile e per esperienza posso dire di essere d'accordo.
Sarà che io lavoro prevalentemente con POJO/POCO ma secondo me se vuoi che l'informazione esca dalla classe almeno un qualche get ce lo devi mettere.
Il tutto naturalmente ragionando in ottica OOD, altrimenti rendo tutti i membri public che poco cambia dall'avere una serie di get/set composti da una sola riga (anche questo viene accennato nell'articolo).
E cito:
"Since accessors violate the encapsulation principle, you can reasonably argue that a system that heavily or inappropriately uses accessors simply isn't object oriented. If you go through a design process, as opposed to just coding, you'll find hardly any accessors in your program"
Poi penso a come sono state realizzate le librerie standard di .Net e Java a come sono fatte le API di qualunque cosa e mi consolo vedendo che fanno ampio ricorso a pratiche contro l'ottica OO secondo la visione esposta da questo articolo...
Lo sa l'oggetto qual è il totale. Se servono operazioni su quel totale, devi dirlo all'oggetto di eseguirle, non chiedere il totale ed eseguire le operazioni esternamente. Questo è il concetto espresso.
Si' ma o Money mi contiene tutte le operazioni che posso effettuare (che non ha senso, ad esempio se devo effettuare la somma dei totali per vedere quanto spende l'azienda), oppure devo passare un oggetto che effettui l'operazione. Ma a questo punto invece che avere legato il mio oggetto all'implementazione ci ho legato l'intermediario. Se ad esempio decido di usare qualcosa di serio per rappresentare i soldi invece che una stringa (!) devo cambiare l'interfaccia (perche' passera' un tipo diverso da String come argomento all'intermediario) e di conseguenza tutte le implementazioni dell'interfaccia... semplifico veramente le cose ?
Come viene chiaramente detto negli articoli, e poi io mi fermo, il design si basa su una serie di scelte. Non c'è scritto di non usare mai get o set. Secondo il suo parere, ma anche quello di altri "illustri", laddove si possa evitare, andrebbe evitato. Viene detto che quasi sempre questo è possibile e per esperienza posso dire di essere d'accordo. Il tutto naturalmente ragionando in ottica OOD, altrimenti rendo tutti i membri public che poco cambia dall'avere una serie di get/set composti da una sola riga (anche questo viene accennato nell'articolo).
Secondo me l'articolo non spiega bene perche' laddove possibile get e set andrebbero evitati. O meglio, lo spiega, ma lo fa con una assuzione a mio avviso sbagliata.
Gli attributi Name e Money dell'oggetto Employe di cui si parla nell'articolo non sono dettagli implementativi della classe, fanno parte dell'interfaccia pubblica dell'oggetto. Posso far finta che non sia cosi' e nascondermi dietro oggetti da passare come argomento per fare le operazioni, ma questo non cambia il fatto che nel momento in cui vado a cambiare la rappresentazione pubblica di una di queste proprieta' (che puo' essere diversa da quella privata, ed e' per questo che uso get/set invece che un campo) devo cambiare tutti gli oggetti che ci interagiscono. Questo sia che lo faccia tramite get/set che tramite una interfaccia (e una implementazione).
Kralizek
09-11-2011, 16:47
io trovo che quell'articolo porti solo ad essere schiavi di una definizione estremista.
per me una classe é una struttura dati e l'insieme delle operazioni su di essa definite.
Un po' come l'insieme dei numeri interi che definisce alcune operazioni ma non altre.
detto ció,
io, in piena filosofia .net, preferisco muovermi su classi dati (piene di properties, possibilmente automatiche) e classi "logiche" che consumano le classi "dati" e restituiscono altre classi dati.
sono rimasto arretrato?
tomminno
09-11-2011, 17:28
io, in piena filosofia .net, preferisco muovermi su classi dati (piene di properties, possibilmente automatiche) e classi "logiche" che consumano le classi "dati" e restituiscono altre classi dati.
sono rimasto arretrato?
Anch'io preferisco lavorare così.
A me non piace molto una classe dati che contiene il metodo Save/Load e quant'altro, perchè significa che la classe deve sapere come caricarsi e salvarsi e in un contesto in cui il come e il dove è altamente variabile preferisco avere una classe apposita per il salvataggio/lettura che utilizza una classe POCO per sapere cosa deve salvare.
@marco.r ma d'altra parte quell'esempio di Property mi ha dato un'idea per una serializzazione trasparente...
e se le properties fossero sincronizzate su file o cloud man mano che le accedi?
Sarebbe un modo di avere metadati sui membri di una classe in C++
/randomrambling
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.