Newsgroup: it.lavoro.informatica
Titolo: Getters, setters and extends are evil!

Moltissimi anni fa, Holub scrisse un paio di articoli che hanno dato vita ad una lunga e accesa diatriba riguardo all'object-orientation:
 
http://www.javaworld.com/article/2073723/core-java/why-getter-and-setter-methods-are-evil.html
 
http://www.javaworld.com/article/2073649/core-java/why-extends-is-evil.html
 
 
Holub ci scrisse anche un libro interessante: 
 
https://www.amazon.co.uk/Holub-Patterns-Learning-Design-Looking/dp/1430253614/ref=sr_1_fkmr0_1?ie=UTF8&qid=1497272204&sr=8-1-fkmr0&keywords=alex+holub+patterns
 
 
Ascoltando persone del calibro di Robert Martin, David West o Eric Evans si ha impressione che gran parte degli sviluppatori OO oggi abbiano preso una strada totalmente diversa da quella indicata da pionieri come Alan Kay. 
 
Alan Kay stesso si e` pentito di aver coniato il termine "object-oriented" dicendo che l'avrebbe dovuta chiamare "message-oriented" e secondo me aveva decisamente ragione!
 


Non   si tratta del diavolo
Si tratta di sapere progettare bene.
Quindi usarle bene.
Su Key non sono d'accordo
Una classe e' una fabbrica di oggetti
E cosi come gli oggetti della vita reale rispondono a comandi od 
emettono informazioni gli oggetti sw sono identici da questo punto di vista.
 
 
 
 

come gli oggetti della vita reale  
 
C'è tutta una teoria su come si progetti un modello di classi per un determinato dominio. Leggi domain-driven design Gli oggetti in un sistema informatico *RAPPRESENTANO* entità della vita reale ma, proprio perché rappresentasioni, soffrono delle constraint del sistema in cui albergano. Essendo rappresentazioni, non necessariamente c'è corrispondenza precisa con le entità rappresentate e non necessariamente godono delle stesse dipendenze da altre entità. Il classico esempio è la classe Quadrato che eredita da Rettangolo: un errore di progettazione fondamentale.

  Il classico esempio è la classe Quadrato che eredita da Rettangolo: un errore di progettazione fondamentale.
 
Questa mi ha incuriosito. Non la conosco proprio.
Perché si tratta di un errore di progettazione?
 
-- 
Ciao!
Luca


Vedi che succede ad andare sui Lagorai invece di studiare OOP? :D
 
Violazione del Liskov substitution principle. Principle a parte, ti 
troveresti a poter settare lato A e B in un quadrato.
 
Se pero' entrambi fossero immutable avresti qualcosa di non piu' fatale.
 
Questo mi fa venire in mente che e' proprio questo la radice di molti 
male: lo stato mutabile. Potessimo lavorare agevolmente con strutture 
dati immutable, ne guadagneremmo un sacco. Purtroppo in Java e' 
praticamente impossibile farlo in modo consistente.

 Holub ci scrisse anche un libro interessante:
 
 
ho letto un po' troppo di fretta, ma tu hai capito come intenderebbe 
renderizzare nome e cognome di un utente ad esempio?
 
user.getNameRenderer()
 
altrimenti come ci accedi ai campi private?
 
In Java almeno, mi pare macchinoso come non mai. Avere uno user con 
property leggibili e settabili (ecco, magari queste si sono un problema) 
da passare alla view che ne fa quel che vuole e' piuttosto comodo.

On 13/06/2017 08:29, 4ndre4 wrote:
[...]
 > Leggi domain-driven design Gli oggetti in un sistema informatico
 > *RAPPRESENTANO* entità della vita reale ma[...]
 
Nel GoF "Design Pattern" si fa notare che questo non e' sempre
vero. Oggetti astratti (non nel che sono senza implementazione)
possono concorrere alla composizione di un problema pur non
rappresentando corrispettivi reali.
 
Comunque sono certo piu' intuitivi quelli che hanno il
corrispettivo reale e li prefersico perche' i miei
modesti neuroni ci ragionano piu' facilmente.

 rappresentando corrispettivi reali.
 
Ma questo e` ovvio. Io sto dicendo che gli oggetti che qualcuno ritiene mappino esattamente degli oggetti reali, in realta` non lo fanno: sono solo rappresentazioni. Poi, che a questi si possano aggiungere oggetti che NON rappresentino oggetti reali e` scontato.

 
 
 
Oh! Scusa, era sottinteso certo.
 
Invece che gli oggetti scritti un linguaggio di programmazione
siano modeste rappresentazioni di quelli reali (quando c'e' una
corrispondenza) lo hai chiarito bene. Sono d'accordo.
 
S'impara sempre qualcosa!

 troveresti a poter settare lato A e B in un quadrato.
 
AAAAHHHHH!!!! Questo, era!
Qui i Lagorai non c'entrano, c'entra la dose di prosecco di ieri sera.
Ho fatto in effetti una domanda senza pensarci. E, vista la risposta, confesso, mi vergogno pure un po' (parecchio) per averla fatta, la domanda. Chiedo venia.
 
-- 
Ciao!
Luca
 

 Perché si tratta di un errore di progettazione?
 
Perche`, anche se in astratto e` vero che un quadrato e` un rettangolo, non e` vero che la rappresentazione di un quadrato e` la rappresentazione di un rettangolo. Il problema e` che gli oggetti in programmazione *rappresentano* delle entita`, non equivalgono ad esse, e quindi non necessariamente ne condividono le relazioni. Un esempio scherzoso citato da Uncle Bob e` che se un avvocato ti rappresenta in una causa di divorzio, non necessariamente sta divorziando anch'egli :)
 

 altrimenti come ci accedi ai campi private?
 
Il punto e` proprio quello: non devi accederci. Dovrebbe essere il tuo oggetto che decide cosa far vedere di se` e come farlo vedere.
 
Credo che per il caso specifico delle UI, Holub consigli di usare un "exporter", che altro non e` che un builder.
 
In sostanza, se hai qualcosa del genere:
 
public class Customer {
    String firstName;
    String lastName;
    Address address;
 
    public Customer(String firstName, String lastName, Address address) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.address = address;
    }
 
    public interface Exporter extends Address.Exporter {
        void addFirstName(String firstName);
        void addLastName(String lastName);
    }
 
    public void export(Exporter exporter) {
        exporter.addFirstName(firstName);
        exporter.addLastName(lastName);
        address.export(exporter);
    }
}
 
Address avrebbe a cascata il suo exporter, e cosi` City e PostCode:
 
public class Address {
    String mainLine;
    City city;
    PostCode postCode;
 
    public Address(String mainLine, City city, PostCode postCode) {
        this.mainLine = mainLine;
        this.city = city;
        this.postCode = postCode;
    }
 
    public interface Exporter extends City.Exporter, PostCode.Exporter {
        void addMainLine(String mainLine);
    }
 
    public void export(Exporter exporter) {
        exporter.addMainLine(mainLine);
        city.export(exporter);
        postCode.export(exporter);
    }
}
 
... da qualche parte istanzi un Customer.Exporter (o piu`) che renderizza come gli pare. 

 
 
Qui sono d'accordo con Holub,almeno quando serve.
Inutile complicare quando non serve.
Pratiche di questo genere sono normali per me.
 

 }
Io ho capito diversamente. L'articolo dice di usare le interfacce, 
ovvero Customer implementa la/le interfacce che ne descrivono le 
Responsibilities pubblicando i metodi che Customer deve implementare - 
export, print, store e chi piu' ne ha piu' ne metta - ovvero comunicare 
coi suoi Collaborators. Quindi Customer non e' riconosciuto dal nome 
della classe concreta ma dalle interfacce che implementa. Per il resto 
mi sembra che Customer debba seguire le regole dei pojo col suo bravo 
costruttore di default ed i suoi accessor.
D'altra parte, ad un certo punto Holub dice:
"You shouldn't use accessor methods (getters and setters) unless 
absolutely necessary..."
Quindi si cade nel citerio assolutamente soggettivo di chi decide quando 
sia "absolutely necessary".
Non a caso Holub fa' l'esempio del JDBC, che definisce solo interfacce, 
ma anche col JDBC ad un certo punto devi dire alla connection come e a 
chi connettersi, quindi la connection ha i suoi bravi accessor.

 Non a caso Holub fa' l'esempio del JDBC
 
Ma infatti le librerie generiche (come lui stesso dice) sono un'eccezione, un caso a parte.

 Inutile complicare quando non serve.
 
Non si tratta di complicare o non complicare. Non hai compreso il punto. Si tratta di seguire approcci diversi. Usare un exporter/importer e` sicuramente piu` complicato (o almeno piu` macchinoso) che usare geters/setters ma almeno non rompe le regole di incapsulamento.

 Ma infatti le librerie generiche (come lui stesso dice) sono un'eccezione, un caso a parte.
 
E' comunque la metodologia a cui tendo. Ma non mi sembra che ci sia 
qualcosa di sbagliato o poco OO. Soprattutto quando i setter sono 
dedicati a configurare il comportamento della classe.
 
 

 dedicati a configurare il comportamento della classe.
 
Hummm.. in quel caso io preferirei passare al costruttore o usare il builder pattern.


Capisco cosa intendi. In effetti non e' che applico lo spesso pattern in 
ogni occasione. Ad esempio, le classi che sono soggette ad essere 
costruite mediante Spring, che utilizzo in modo pressoche' 
generalizzato, le disegno perche' meglio si adattino al pattern che 
Spring impone. Capisco anche che magari la soluzione complessiva non sia 
perfettamente OO, pero' devo pur concedermi qualche agio!
:-D

 costruite mediante Spring, 
 
Eh, ma in che modo Spring ti impedirebbe di applicare quanto detto prima?
Con l'autowiring, Spring facilita la dependency-injection, ma mica ti obbliga ad usare getters/setters. Magari non ho afferrato, mi fai un esempio?
 


Senza autowiring sei obbligato, altrimenti non riesci ad impostare 
alcuna <property>. E l'autowiring non trovo che sia generalmente 
utilizzabile, soprattutto perche' obbliga ad intervenire sui sorgenti.


Grazie. Mi pare di capire che il CustomRenderer dovrebbe ricevere e 
copiare tutti i campi *esplosi*, il che lo trovo estremamente 
inconveniente. Pensa che caos quando ti trovi ad avere firstName, 
lastName, city, mainLine, etc come campi non raggruppati comodoamente in 
oggetti dentro il renderer.
 
Alla fine mi sembra di capire che se si potesse semplicemente passare 
Customer al CustomerRenderer e se solo CustomerRenderer potesse leggere 
i campi (con regole di visibilita' tipo il companion object di Scala), 
avremmo una soluzione praticabile. Aggiungiamo anche che Customer e 
Address dovrebbe essere immutable.
 
E mi domando anche come mai non si faccia evolvere il linguaggio 
cercando di semplificare pattern di questo tipo.

 estremamente inconveniente
 
lol ma come cazzo scrivo.

 i campi
 
Eh, ma si ritorna alla rottura dell'incapsulamento. I companion objects sono l'equivalente delle friend classes del C++, un altro "big no no".

 lol ma come cazzo scrivo.
 
Troppo inglese :)
 


Chiuppilupetutti.
:-)