[CPP] MAMMA MAMMA I MIEI MEMBRI SONO INCONSISTENTI

Stato
Discussione chiusa ad ulteriori risposte.

orakool

Utente Electrum
30 Giugno 2007
323
6
2
118
Ho recentemente avuto una discussione con un tizio che sosteneva l'importanza di avere sempre funzioni set* e get* invece di rendere pubblici i membri di una classe ove serva l'accesso a questi membri, perche` senno` c'e` il rischio che suddetti membri diventino #{metti qui il topic}.

A tal proposito ho scritto un piccolo esempio di codice, commentato per sottolineare lo scopo.

http://sprunge.us/ebcW?cpp

vorrei sapere che ne pensate voi, sono pazzo? Secondo me un membro va messo private quando deve essere PRIVATO, ovvero quando nessuno a parte me che scrivo la classe deve accedervi.

Discuss

EDIT: il metodo setTua getMamma e` quello che insegnano nelle universita`.
 
il ragionamento che c'è dietro è corretto (almeno secondo me).
Invece di far accedere direttamente ai membri, che verranno usati dall'oggetto, e dare la possibilità di giocarci come si vuole, è meglio introdurre un membro che possa fare un controllo di validità dei dati.

Ovviamente poi dipende dal membro, ma il ragionamento di prima rimane corretto
 
Hai letto il sorgente? Il problema non e` che non vada fatto un controllo sull'accesso al dato membro, ma che non c'e` bisogno di farne un metodo da richiamare, quando puoi ridefinire l'operatore che normalmente farebbe danni facendo in modo che invece faccia dei controlli e mantenga consistente il dato membro.

Voglio dire, che senso ha fare un oggetto privato se poi privato non e`, dato che puoi sia leggerlo che scriverci? Il senso di un dato privato, secondo me, e` che chi usa la mia classe non deve mai averne bisogno, lo uso io per fini implementativi piu` che altro. Mentre su tutti i manuali e` consigliato di fare privati TUTTI i dati membri indistintamente, e creare le solite set e get che, in caso di oggetti complessi, generano per lo piu` un overhead non indifferente.

EDIT: Inoltre, componendo nella mia classe oggetti di altre classi come dati membro, e creando metodi nella mia classe per operare su questi dati membro, sarebbe come dire che l'oggetto non e` l'entita` a se stante che la teoria dell'OOP professa, ma diventa dipendente a seconda dell'uso che se ne fa.

Ovviamente tutto cio` non si applica ai tipi base.
 
guarda quello che ho detto io è la spiegazione del motivo per cui si fa un membro privato.

Il concetto è chi costruisce la classe sa come usare il membro (lettura o scrittura), mentre chi la usa potrebbe non saperlo.
Mettere pubblico un variabile membro, permette a chiunque di modificarla a piacimento, mentre mettendola privata e scrivendo delle funzioni get e set puoi inserire, SE NECESSARIO, eventuali controlli.

Il motivo per cui si raccomanda di fare così, credo sia solo questo, cioè rendere accessibile un membro, ma attraverso funzioni che possono fare, tra le altre cose, controlli, lanciare eccezioni.

un esempio, io voglio che un membro sia un numero tra 1 e 10. Lasciare quel membro pubblico, significa che un utente può anche metterci 55564 e questo implica, tra l'altro, che devo fare controlli ad ogni funzione che la utilizza.
Invece rendendola privata, posso introdurre membri (set) che faccia dei controlli.

In aggiunta posso rendere un membro anche readonly solo dall'esterno (creando solo la funzione get), o solo writeonly (solo funzion set), o anche write once read many (così si dice?), implementando opportunamento la fuzione set
 
Non hai capito.
Esegui anche il codice allora, perche` non l'hai capito quello che fa ^^ non e` possibile assegnare una stringa piu` lunga di 10 caratteri al membro pubblico mystring, il controllo su cio` che gli si assegna c'e` eccome.
 
si, tu hai chiesto il perchè e io ti ho risposto, ma se leggi il mio primo post:
Ovviamente poi dipende dal membro.
nel tuo caso (che è cmq una situazione particolare) non potresti accedere tramite = () ecc. perchè ridefinite da te.

MA, se guardiamo bene, il tuo è un caso EVIDENTE di perchè si DEVE mettere il membro privato^^
c'è un caso (anzi 2) che non hai considerato che sputtana gli eventuali controlli in quel sorgente (ovviamente se _string è pubblica)

:EDIT:
in realtà 1 solo, l'altro è un problema che c'è anche se il membro è privato, ma non riesco a capire perché

:EDIT2:
ho capito, usa il costruttore, che non ha i controlli (quindi niente di strano, c'è cmq l'altra situazione a cui non hai pensato che da problemi)
 
Quale altra situazione? E poi ovviamente _string non lo puoi definire pubblico, visto che e` soggetto a sputtanamenti, non puoi fare alcun tipo di prevenzione su quello che gli viene assegnato, _in teoria_

Quello sopra era un esempio poco concreto, per farmi capire meglio:
Poni il caso che invece quello che ti serve e` esattamente una classe che contenga una stringa che non superi mai i 10 caratteri di lunghezza, normalmente cosa faresti? Scrivi la classe, ci metti un std::string come membro privato e nel Tuaclasse::seString controlli che non le venga assegnato un valore superiore a 10. Questo e` il metodo che insegnano la maggior parte della gente.

Io invece farei cosi`.
http://sprunge.us/ROSi

ovvero, definisco un nuovo tipo, una nuova classe, che mi consenta di tenere sotto controllo cosa le viene assegnato, senza dover costruire della roba sulla prima classe (quella in cui mi serve la stringa) che fa dei controlli di non sua competenza.

Poi boh, saro` scemo, ma secondo me e` sia piu` pulito che piu` logico.
 
è una soluzione, ma molto pericolosa.... devi conoscere tutti i mebri e li devi anchi ridefinire a volte (e poi potrebbero esserci problemi nel caso di puntatori legati alla presenza o alla mancanza della parola chiave virtual).

infatti nell'esempio tuo (ROSi) c'è un metodo semplice per aggirarlo, anzi tanti: insert, replace, append,ecc.
http://sprunge.us/gQQQ

:EDIT:
ovviamente è fattibile, ma non è molto più semplice ed ugualmente efficace gestirlo con 2 funzioni e metterlo privato (ovviamente dipende anche da quante funzioni della classe esistente devi usare e gestire)?
 
@tilde: sai che il tuo metodo è molto pythoniano? (tra l'altro hai perfino chiamato il privato _string, seguendo quindi incosciamente le py-linee_guida)

ormai sei stato contagiato, bwahahahaha
 
Whivel ha detto:
è una soluzione, ma molto pericolosa.... devi conoscere tutti i mebri e li devi anchi ridefinire a volte (e poi potrebbero esserci problemi nel caso di puntatori legati alla presenza o alla mancanza della parola chiave virtual).

infatti nell'esempio tuo (ROSi) c'è un metodo semplice per aggirarlo, anzi tanti: insert, replace, append,ecc.
http://sprunge.us/gQQQ

:EDIT:
ovviamente è fattibile, ma non è molto più semplice ed ugualmente efficace gestirlo con 2 funzioni e metterlo privato (ovviamente dipende anche da quante funzioni della classe esistente devi usare e gestire)?

Beh, per difendermi in un colpo solo da tutte le funzioni alternative che possono operare sull'oggetto invalidandolo basta ereditare private o protected, intanto.

In secondo luogo, se tu fai due funzioncine per accedervi sacrifichi l'intero oggetto, tutti il codice gia` scritto per operare su una stringa non serve piu` a niente.

Immagina, per esempio, che io faccia una classe per il calcolo e la manipolazione del codice fiscale, in cui la stinga deve avere dei precisi formati e contenere cose precise. Se io mettessi una normale stringa come dato privato e altri metodi per accedervi il lettura e scrittura non farei altro che aggiungere un layer in piu`, per giunta nel posto sbaglato. Se invece creassi una classe apposita, public-safe, specializzata per fare il suo lavoro, non avrei bisogno d'altro nella classe client, avrei gia` a disposizione un oggetto pienamente autosufficente e riusabile, come la teoria della OOP consiglia.

EDIT: Tra l'altro comincio ad avere dei seri dubbi sulla vera utilita` del mascheramento delle informazioni, il concetto secondo il quale io devo utilizzare una classe seguendo la sua interfaccia (definita nell'header) e fregandomene di come le cose sono implementate. Certamente ha senso in un ambito commerciale, ma pone anche un limite: se io sapessi esattamente come una classe implementa i suoi metodi, potrei procedere a ereditare e ridefinire secondo le mie esigenze con molta piu` comodita`.

Insomma, prendi append, basta fare il controllo ed eventualmente richiamare il metodo della classe parente. E` vero che i metodi pubblici che modificano l'oggetto possono essere anche centomila, in effetti sarebbe bello avere un sistema automatizzato per questa roba.
 
Io credo che non e' un fatto di "la gente che usa la mia classe non sa come usarla quindi faccio i controlli con i miei metodi", ma e' piu un fatto di: la gente dovra' usare la mia classe, e se ne deve fregare di come e' fatta, anche perche' se prima di utilizzare ogni singola classe mi devo mettere a vedere come e' fatta stiamo freschi... chi fa la classe gli dara' quindi un interfaccia stabile, e quindi eventuali cambiamenti interni di implementazione alla classe non si ripercuotono sulle altre classi che la usano... e' da tale concetto che poi e' venuto fuori http://en.wikipedia.org/wiki/GRASP_(object-oriented_design)#Protected_Variations
 
tilde ha detto:
Ho recentemente avuto una discussione con un tizio che sosteneva l'importanza di avere sempre funzioni set* e get* invece di rendere pubblici i membri di una classe ove serva l'accesso a questi membri, perche` senno` c'e` il rischio che suddetti membri diventino #{metti qui il topic}.

A tal proposito ho scritto un piccolo esempio di codice, commentato per sottolineare lo scopo.

http://sprunge.us/ebcW?cpp

vorrei sapere che ne pensate voi, sono pazzo? Secondo me un membro va messo private quando deve essere PRIVATO, ovvero quando nessuno a parte me che scrivo la classe deve accedervi.

Discuss

EDIT: il metodo setTua getMamma e` quello che insegnano nelle universita`.
N.B. non ho letto le risposte, quindi perdonatemi se dico qualcosa di già detto
Io direi: dipende.
Prendo d'esempio questo codice
Codice:
class C
{
  char m_p[10];
public:
 char getFirst() const
 {
    return m_p[0];
 }
};
Lasciando p privato, e magari settandolo con un valore di default al costruttore, chiamare getFirst è sempre sicuro.
Se lasciamo p pubblico però, le cose si complicano: se qualche bravo furbo fa c.m_p=0; prima, ci ritroviamo con un bel sigsev. A meno che il programmatore non si fidi dell'utilizzatore della classe, sarebbe bene lasciare p privato e permettere di modificarlo attraverso un metodo che comprenda i vari check.
Il tuo codice non mi pare sia così problematico: che ci sia un set* o un operator= è la stessa identica cosa, sempre di set si tratta
Naturalmente con le string non servirebbe a meno che tu non voglia, come in questo caso, limitarne la lunghezza massima.
In questi casi io fornisco entrambe le soluzioni, impelementando il set in set*() e rendendo operator= un metodo inline che richiama set. In pratica si tratterebbe di un syntax sugar, ma sempre set è.
Spero che il problema sia questo sennò ho scritto sto papiro per niente XD
 
@vheon, l'enunciato "anche perche' se prima di utilizzare ogni singola classe mi devo mettere a vedere come e' fatta stiamo freschi..." e` discutibile, ma ho capito che intendi il conoscerne l'interfaccia, e infatti non sto dicendo che devi conoscerne l'implementazione, solo che devi farne un uso ragionevole.

Il solito esempio delle stringhe, se io faccio una classe per la gestione e manipolazione delle stringhe, e a te serve una classe che abbia come dato membro una stringa da 10 caratteri max, invece di mettere il membro privato e nell'interfaccia i metodi per l'accesso al membro (che fanno il controllo sulla lunghezza), tu ti crei un'altra classe che eredita dalla mia e fa dentro di se` i controlli sull'integrita` dei suoi dati. Dico solo che fare controlli sui membri in una classe client e` mala programmazione, secondo me.

@Lumo, non hai capito. Se metti un set o ridefinisci l'operatore _cambia_. Ovviamente tu hai una classe C che utilizza quella stringa c-style, ma non e` che puoi ridefinire l'operatore = per quella classe per andare a modificare quel membro (ammesso che tu non stia esattamente facendo una classe per la gestione di quel preciso dato, come una std::string), capisci da te che non avrebbe senso, chissa` cosa fa quella classe. Qundi tu prima ti fai un'altra classe che gestisce il dato m_p, poi utilizzi quella classe dentro alla classe C.
 
l'idea è che se ho due membri che devono lavorare in coppia non devo permettere all'utente (programmatore) di modificarne solo uno sputtantando, che so, l'integrità referenziale di una base di dati. Allora uso dei metodi get/set che si occupano di /sarcazzi/ in modo che non mi si sputtani tutto il db.

Che poi spesso sia superfluo è un dato di fatto ma è come le parentesi graffe dopo il costrutto if, me4glio metterle che prima o poi modifichie ti fanno comodo.

Inoltre è una metodologia ereditata da Java, in cui puoi serializzare gli oggetti su file se hai tutti i metodi definiti.


Insomma, bisogna usare criterio è l'unica risposta plausibile, dopotutto è il lavoro del developer!
 
@malex, infatti no. Ma in un certo senso ha senso, dato che in java non puoi overloadare gli operatori come in C++, quindi il mio discorso non si applica del tutto.

@den, pure tu non hai capito il mio discorso: io _NON_ sto dicendo che non vadano fatti controlli per l'integrita` dei dati, dico che cosi` come si fa ora non e` il metodo piu` pulito/comodo/conforme alla logica ad oggetti. Comunque, il tuo discorso sulla serializzazione non lo capisco proprio. Mentre non conosco il java, il C++ non ha metodi propri del linguaggio per la serializzazione, allo stesso modo non conosco librerie che offrono questa funzionalita` che richiedano anche tale prerequisito per l'utilizzo (vedi, che so, boost/archive).
 
@tilde, a questo punto credo di aver capito cosa vuoi dire: get/set vs overload di operatori. La cosa importante credo sia che, vedendo del codice scritto che utilizzi la tua classe, si deve capire al volo quello che vuoi fare e che sia cmq nel tempo facilmente manutenibile... Quindi dato che per ora non ho grande esperienza con il C++ non ti so dire se hai ragione o meno...
 
imho, la soluzione migliore (almeno, fra quelle proposte dai vari linguaggi di mia conoscenza) è quella del C#, ossia "includere" il get/set di per sé negli attributi e quindi poter, ad esempio, avere un attributo leggibile ma nn scrivibile, avendo il get public ed il set private (per fare un esempio). Lo trovo il sistema + elegante (insieme al property del python, ma questo ovviamente molto + imho).
 
Malex ha detto:
imho, la soluzione migliore (almeno, fra quelle proposte dai vari linguaggi di mia conoscenza) è quella del C#, ossia "includere" il get/set di per sé negli attributi e quindi poter, ad esempio, avere un attributo leggibile ma nn scrivibile, avendo il get public ed il set private (per fare un esempio). Lo trovo il sistema + elegante (insieme al property del python, ma questo ovviamente molto + imho).

O quello del Ruby e del Javascript :3
 
meh. ha detto:
Malex ha detto:
imho, la soluzione migliore (almeno, fra quelle proposte dai vari linguaggi di mia conoscenza) è quella del C#, ossia "includere" il get/set di per sé negli attributi e quindi poter, ad esempio, avere un attributo leggibile ma nn scrivibile, avendo il get public ed il set private (per fare un esempio). Lo trovo il sistema + elegante (insieme al property del python, ma questo ovviamente molto + imho).

O quello del Ruby e del Javascript :3

avevo specificato. Purtroppo ruby e javascript non rientrano al momento nelle mie conoscenze.
 
Malex ha detto:
meh. ha detto:
Malex ha detto:
imho, la soluzione migliore (almeno, fra quelle proposte dai vari linguaggi di mia conoscenza) è quella del C#, ossia "includere" il get/set di per sé negli attributi e quindi poter, ad esempio, avere un attributo leggibile ma nn scrivibile, avendo il get public ed il set private (per fare un esempio). Lo trovo il sistema + elegante (insieme al property del python, ma questo ovviamente molto + imho).

O quello del Ruby e del Javascript :3

avevo specificato. Purtroppo ruby e javascript non rientrano al momento nelle mie conoscenze.

Infatti ti aggiungevo conoscenze :3
 
Stato
Discussione chiusa ad ulteriori risposte.