Newsgroup: it.comp.java
Titolo: Java e Incapsulamento

Ciao a tutti
non sň come poter risolvere il problema:
ho una classe con un membro connection privato ed un metodo che restituisce 
la connessione da utilizzare altrove.
In questo modo funziona il tutto, perň ho violato uno dei paradigmi della 
OOP.
C' č un modo per risolvere il problema??
Ciao 

> C' è un modo per risolvere il problema??
tua classe e' abbastanza corretta
inutile che wrappi la connection che e' gia' un wrapper jdbc
il fatto che la connection e' privata e il metodo pubblico ti da i
vataggi della programmazione OOP :
per esempio se tu vuoi che ogni volta che uno chiede la Connection
esca un banner con scritto "La fica e' bella" , con la tua classe metti il
codice del banner nel metodo getConnection pubblico.
Se davi la connection direttamente non potevi.
  ciao, e leggi qualche paginetta di OOP programming
N
--------------------------------
Inviato via 
http://arianna.libero.it/usenet/

 OOP.
???? cosa????
Non hai violato nulla, hai creato una classe che si mastruzza la
connessione, L'unica cosa che puoi fare è reperire la connessione!
Hai applicato correttamente l'incapsulamento.

 OOP.
No, non hai violato alcun paradigma.
Incapsulamento ed occultamento prevedono proprio che gli attributi siano
privati ed accessibili solo con apposite funzioni di lettura o scrittura,
capaci di elaborare propriamente gli attributi a seconda
dell'implementazione sottostante.
Che poi tu restituisca il tuo attributo "grezzo", è un dettaglio della tua
implementazione (giusto o sbagliato dipende da quello che devi fare)  che
comunque l'utilizzatore della classe non conosce, perchè appunto privato,
occultato.
Spero di essere stato chiaro.
Un saluto,
Sergio B.
-- 
Sergio Bossa
Author and Developer of:
- Montag, Web Services System for XML Database Interaction:
 
http://montag.sourceforge.net
- QuickNote:
 
http://quicknote.sourceforge.net

[cut]
Beh è una violazione perchè da esterno ottengo un campo privato della 
classe,
il che significa che se
connection conn = object.getConnection();
conn.close(); // effetto collaterale!! la classe ha subito una modifica di 
un campo accessibile solo dalla classe stessa!!!
è violazione dell'incapsulamento. 

> č violazione dell'incapsulamento.
contento te
ti abbiamo risposto in 3 la stessa cosa e gli altri 2 li leggo qua
regolarmente
non credo a questo punto che qualcuno abbia il tempo di dscutere oltre
   ciao , studia di + i ilibri, e ascolta di + i suggerimenti,
se no parti male con java
 
               cya NIcola


 > è violazione dell'incapsulamento.
comunque se proprio vuoi:
fai un interfaccia con solo alcuni dei metodi della connection
per sempio tutti meno la close()
a questo punto restituisci l'interfaccia tramite il metodo getConnection
e non la connectio diretta
cosi' se un giorni usi una connection di un altro package o addirittura non
DB
ma File o Socket o quello che vuoi
il tuo client funzionera' ancora
purche la nuova connection implementi la stessa interfaccia
 ciao N


 per sempio tutti meno la close()
dimenticavo:
per implementare l'interfaccia devi anche estendere la connection con una
tua
class ficaConnection extends Connection implements ficaConnectionInterface
e scusa per il tono di prima


 se no parti male con java
Veramente non è per polemica, ma l'ho anche letto su
un libro ufficiale della Sun pubblicato da Mc.grawhill.
Inoltre sviluppo già con OO su C++ dove questo problema
non sussiste.

 non sussiste.
Anche io ho sviluppato in C++ e, lungi da me il voler fare polemiche, mi
sapresti dire in che senso in tale linguaggio questo problema non sussiste?
Forse ti riferisci alla possibilità di dichiarare metodi "const" e
restituire oggetti costanti, con la conseguenza di poter chiamare solo i
metodi dichiarati tali...è una feature di cui, quando sono passato a Java,
ho sentito molto la mancanza, ma è una (buona) tecnica di programmazione
che pochissimi seguono e non è detto che tutte le classi di terze parti la
osservino.
O ti riferisci al fatto che in C++ puoi restituire copie degli oggetti, e di
conseguenza la tua close() non avrebbe effetto sulla Connection privata
della classe? Fammi sapere se ti riferisci a questo perchè qui c'è un
piccolo tranello....
O se ti riferisci ad altro ... sono curioso.


 Un saluto,
Mah...se proprio devo violare l'incapsulamento come in java, restituendo un 
oggetto privato, penso sia meglio
come nel C++ creare tale oggetto statico all'interno del metodo e 
restituirlo non pensi????
Ciao 

 restituirlo non pensi????
Questa non l'ho capita, cosa intendi?
Spiegati meglio, magari.


 connection conn = object.getConnection();
Per quale motivo dovresti restituire una connection privata? Se vuoi 
interagire con la connection *privata* andrai a definire dei metodi 
public che delegheranno alla connection le operazioni da compiere.
Ciao.

AlterX wrote:
 > [cut]
 >
 > > Anche io ho sviluppato in C++ e, lungi da me il voler fare
 > > polemiche, mi sapresti dire in che senso in tale linguaggio questo
 > > problema non sussiste?
 > > Forse ti riferisci alla possibilità di dichiarare metodi "const" e
 > > restituire oggetti costanti, con la conseguenza di poter chiamare
 > > solo i metodi dichiarati tali...è una feature di cui, quando sono
 > > passato a Java, ho sentito molto la mancanza, ma è una (buona)
 > > tecnica di programmazione che pochissimi seguono e non è detto che
 > > tutte le classi di terze parti la osservino.
 >
 > Moolto bene...hai centrato! :-)
Alcuni giorni fa, da qualche parte, ho letto un articolo su questo 
argomento (o forse era un post su questo newsgroup, non ricordo proprio) 
che faceva un'osservazione interessante.
L'utilizzo dei metodi const equivale a spezzare l'interfaccia di una 
classe in due:
In pratica la classe:
public class Mia {
   public void metodoNonConst() {...}
   public const void metodoConst() {...}
}
costruita supponendo di avere il "const" è equivalente alla seguente 
coppia classe + interfaccia:
public interface MiaConst {
   void metodoConst();
}
public class Mia implements MiaConst {
   public void metodoNonConst() {...}
   public void metodoConst() {...}
}
Anzi questa tecnica (ovviamente utilizzabile ed utilizzata anche in C++) 
è più potente in quanto consente di spezzare in modo più dettagliato 
l'interfaccia (perché dovremmo limitarci a una dicotomia const/non const?).
Quindi il problema non è che Java non ha il costrutto "const". Il 
problema è che l'interfaccia Connection risulta troppo "monolitica". Lo 
stesso problema l'avresti avuto in C++ se i metodi fossero stati tutti 
dichiarati non const.
L'unico vantaggio del C++ è di avere una sintassi più "compatta".
Le soluzioni per te, che non puoi modificare l'interfaccia 
java.sql.Connection, sono due:
1) Definire nella tua classe dei metodi che delegano alla Connection le 
funzionalità che devono essere messe a disposizione di quanti usano la 
tua classe. Ad esempio se gli utenti della tua classe devono poter 
costruire un preparedStatement, introduci nella tua classe il metodo:
   public PreparedStatement prepareStatement(String sql)
                   throws SQLException {
     return connessione.prepareStatement(sql);
   }
Lo svantaggio è che non hai a disposizione un oggetto Connection. Magari 
a te serve passarlo come argomento ad un altro metodo e in questo caso 
non puoi farlo.
2) Definire una classe che implementi l'interfaccia Connection e che 
deleghi tutti i metodi, tranne quelli che non vuoi siano disponibili ad 
altri, all'oggetto connection.
Sotto ti riporto il codice di tale classe scritto in 30 secondi 
utilizzando le funzionalità di generazione del codice dell'IDE di Eclipse.
Il principale svantaggio è che il controllo sul fatto che il metodo 
close non deve essere chiamato viene fatto a runtime (anche se ho messo 
un @deprecated per dare comunque una segnalazione in fase di 
compilazione, ma non e' proprio una cosa corretta e su cui fare 
affidamento, soprattutto perché non funziona se uno fa un cast a 
Connection).
Soluzioni ottimali non ce ne sono, ma questo non è dovuto a qualche 
mancanza del linguaggio in se, ma a quello che definirei un "errore" 
(almeno rispetto alle tue esigenze) in fase di progettazione della 
classe java.sql.Connection.
Andrea
P.S.
Il codice di cui parlavo:
--------------------------------------------
class TuaClasse {
   Connection conn;
   ...
   public NoCloseConnection getConnection() {
     return NoCloseConnection();
   }
}
--------------------------------------------
public class NoCloseConnection implements Connection {
   private final Connection conn;
   public NoCloseConnection(Connection conn) {
     this.conn = conn;
   }
   /*
    * @deprecated Il metodo no va richiamato
    */
   public void close() throws SQLException {
     throw new IllegalStateException("Non è possibile chiudere questa
connessione.");
   }
   public void clearWarnings() throws SQLException {
     conn.clearWarnings();
   }
   public void commit() throws SQLException {
     conn.commit();
   }
   public Statement createStatement() throws SQLException {
     return conn.createStatement();
   }
   public Statement createStatement(int resultSetType, int
resultSetConcurrency)
       throws SQLException {
     return conn.createStatement(resultSetType, resultSetConcurrency);
   }
   public Statement createStatement(int resultSetType, int
resultSetConcurrency,
       int resultSetHoldability) throws SQLException {
     return conn.createStatement(resultSetType, resultSetConcurrency,
         resultSetHoldability);
   }
   public boolean equals(Object obj) {
     return conn.equals(obj);
   }
   public boolean getAutoCommit() throws SQLException {
     return conn.getAutoCommit();
   }
   public String getCatalog() throws SQLException {
     return conn.getCatalog();
   }
   public int getHoldability() throws SQLException {
     return conn.getHoldability();
   }
   public DatabaseMetaData getMetaData() throws SQLException {
     return conn.getMetaData();
   }
   public int getTransactionIsolation() throws SQLException {
     return conn.getTransactionIsolation();
   }
   public Map getTypeMap() throws SQLException {
     return conn.getTypeMap();
   }
   public SQLWarning getWarnings() throws SQLException {
     return conn.getWarnings();
   }
   public int hashCode() {
     return conn.hashCode();
   }
   public boolean isClosed() throws SQLException {
     return conn.isClosed();
   }
   public boolean isReadOnly() throws SQLException {
     return conn.isReadOnly();
   }
   public String nativeSQL(String sql) throws SQLException {
     return conn.nativeSQL(sql);
   }
   public CallableStatement prepareCall(String sql) throws SQLException {
     return conn.prepareCall(sql);
   }
   public CallableStatement prepareCall(String sql, int resultSetType,
       int resultSetConcurrency) throws SQLException {
     return conn.prepareCall(sql, resultSetType, resultSetConcurrency);
   }
   public CallableStatement prepareCall(String sql, int resultSetType,
       int resultSetConcurrency, int resultSetHoldability) throws
SQLException {
     return conn.prepareCall(sql, resultSetType, resultSetConcurrency,
         resultSetHoldability);
   }
   public PreparedStatement prepareStatement(String sql) throws
SQLException {
     return conn.prepareStatement(sql);
   }
   public PreparedStatement prepareStatement(String sql, int
autoGeneratedKeys)
       throws SQLException {
     return conn.prepareStatement(sql, autoGeneratedKeys);
   }
   public PreparedStatement prepareStatement(String sql, int resultSetType,
       int resultSetConcurrency) throws SQLException {
     return conn.prepareStatement(sql, resultSetType, resultSetConcurrency);
   }
   public PreparedStatement prepareStatement(String sql, int resultSetType,
       int resultSetConcurrency, int resultSetHoldability) throws
SQLException {
     return conn.prepareStatement(sql, resultSetType, resultSetConcurrency,
         resultSetHoldability);
   }
   public PreparedStatement prepareStatement(String sql, int[]
columnIndexes)
       throws SQLException {
     return conn.prepareStatement(sql, columnIndexes);
   }
   public PreparedStatement prepareStatement(String sql, String[]
columnNames)
       throws SQLException {
     return conn.prepareStatement(sql, columnNames);
   }
   public void releaseSavepoint(Savepoint savepoint) throws SQLException {
     conn.releaseSavepoint(savepoint);
   }
   public void rollback() throws SQLException {
     conn.rollback();
   }
   public void rollback(Savepoint savepoint) throws SQLException {
     conn.rollback(savepoint);
   }
   public void setAutoCommit(boolean autoCommit) throws SQLException {
     conn.setAutoCommit(autoCommit);
   }
   public void setCatalog(String catalog) throws SQLException {
     conn.setCatalog(catalog);
   }
   public void setHoldability(int holdability) throws SQLException {
     conn.setHoldability(holdability);
   }
   public void setReadOnly(boolean readOnly) throws SQLException {
     conn.setReadOnly(readOnly);
   }
   public Savepoint setSavepoint() throws SQLException {
     return conn.setSavepoint();
   }
   public Savepoint setSavepoint(String name) throws SQLException {
     return conn.setSavepoint(name);
   }
   public void setTransactionIsolation(int level) throws SQLException {
     conn.setTransactionIsolation(level);
   }
   public void setTypeMap(Map map) throws SQLException {
     conn.setTypeMap(map);
   }
}

Più o meno hai centrato il problema...
ma la cosa più brutta è che in Java gli
oggetti vengono passati per riferimento 
e questo tranne per String e Date che sono
immutabili e per i tipi primitivi, porta ad 
una rottura dell'information hiding (implementazione INTERNA della classe):
se ho un oggetto privato che viene utilizzato dalla classe
deve rimanere TALE perchè potrebbe compromettere il funzionamento della
stessa; però è anche giusto che posso utilizzarlo all'esterno senza dover
reinventare quello che è già implementato nel metodo della classe, ma in
java non lo si può fare perchè restituisce un riferimento all'oggetto
PRIVATO e questo porta alla possibilità di modificare il comportamento
INTERNO della classe da ovunque.
Per ovviare a questi problemi che il C++ permette di risolvere (o con const
o con la copia etc...) in java non si può fare se non clonando l'oggetto in
questione!!
ma appunto come stai dicendo l'oggetto Connection (anzi interfaccia) non
permette agevolmente di fare una cosa del genere!
Tutto qui!

 permette agevolmente di fare una cosa del genere!
Ma perché è l'oggetto a non rispondere alle tue esigenze, non il 
linguaggio. Se vuoi sentirmi dire che i progettisti della libreria 
standard hanno commesso un errore posso arrivare a dirlo (anche se la 
tua esigenza direi che e' abbastanza particolare), ma non mi venire a 
raccontare che e' il linguaggio (non la libreria) che e' limitato!
Andrea

AlterX ha scritto:


Penso che il tuo sia sostanzialmente un problema che si
risolve a livello di design (ed in modo indipendente dal linguaggio!)
Le possibilita' sono diverse:
1) Pattern Decorator
   Se l'esigenza e' quella di esporre un metodo che restituisca 
   un'istanza di tipo java.sql.Connection, mantenendone sotto controllo
   l'utilizzo, una soluzione tipica e' il pattern Decorator: crei una
   tua implementazione di java.sql.Connection come "wrapper" della
   "vera" Connection che devi restituire. 
   Il "wrapper" delega alla Connection originale l'esecuzione di tutti i  
 
   metodi d'interfaccia, ma ha la possibilita' di tenerne sotto controllo 
   l'utilizzo.
   Ad esempio, una chiamata al metodo close() sul "wrapper" potrebbe
   non fare nulla, oppure sollevare una eccezione, oppure ancora 
   ritornare la connessione originale ad un pool, ecc.
   Qui le possibilita' sono evidentemente infinite...
2) Legge di Demetra
   La tua classe NON ritorna MAI la Connection privata, ma espone
   metodi specifici che forniscono ai client le funzionalita' che
   permettono di interagire (in modo piu' o meno esplicito) con
   la Connection privata.
   Si tratta di un'applicazione della "Legge di Demetra" (si chiama
   legge, ma NON lo e'!) in quanto i client della tua classe 
   interagiranno solo con essa e non con riferimenti indiretti ad
   altri oggetti.
 
   ESEMPIO 1
   ---------
   // In questo caso, TuaClasse restituisce direttamente un 
   // reference a Connection che puo' essere usato dai chiamanti:
   // si ha probabile violazione della legge di Demetra, per 
   // un motivo che potrebbe essere ricondotto a quanto 
   // obiettavi tu circa la violazione dell'incapsulamento
   TuaClasse tuaClasse = new....
   ...
   PreparedStatement ps = tuaClasse.getConnection().prepareStatement();
   ...
   ESEMPIO 2
   ---------
   // In questo caso, TuaClasse espone un metodo che USA 
   // la Connection privata senza esporla. I client NON sanno
   // assolutamente di impiegare una Connection.
   // Cio' rispetta la legge di Demetra, ed e' tutto sommato
   // una buona regola di progettazione del software OO: si chiede
   // agli oggetti di fare qualcosa, invece di richiedere "dati"
   // sui quali operare dall'esterno
   TuaClasse tuaClasse = new....
   ...
   tuaClasse.faiQualcosaConLaConnection();
Quanto cio' sia applicabile al tuo caso dipende dal livello
di astrazione dell'interfaccia della tua classe: se si tratta di
servizi di basso livello che DEVONO restituire istanze di 
Connection che DEVONO essere utilizzate da altri oggetti, allora 
la soluzione 1) e' certamente piu' indicata della 2).
I puristi perdonino eventuali imprecisioni, ma il tempo 
e' tiranno.
Saluti
Roberto
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 Dubito poi che sia questo il risultato che ti interessa.
Volevo tirarmene fuori da sto dibattito ma non resisto.
Riflessioni a freddo del giorno dopo :
1.) L'oggetto Connection NON e' clonabile. L'oggetto Connection e' un
wrapper jdbc sulla Connection "fisica" lato server sul db. mI Spiego.
Trimite il driver si crea una Connection sul db che ha vita propria anche se
la nostra applicazione cade. La Connection e' semplicemente un link ad essa.
Non per niente ci si deve preoccupare di chiuderla.
E non per niente e' stato fatto il pool che le gestisce meglio.
2) AlterX tutto sommato ha le sue ragioni. Una buona classe non dovrebbe mai
dare la Connection in giro, ma solo gli Statement, che lavorano in parallelo
sulla stessa connection. Uno nuovo ad ogni richiesta. Ed esporre al limite
qualche metodo per modificarne i parameteri di funzionamento ( se
autorizzati)
3) Se serve invece una connection per ogni client dlla classe ( per esempio
se vogliono loggarsi con user diverse) bisogna crearsi una struttura +
adeguata che tenga le connection in una Map, un pool, o cose simili.
4) Come scrivevo in un altro post, per incapsulare bene la strada giusta e'
quella di usare le interface, che possono escludere alcuni metodi della
Connection(o qualsiasi elemento) e possono anche in un secondo tempo
sostituire l'elemento incapsulato, senza avere effetti collaterali sui
client che lo usano. basta che l'elemento nuovo implementi l'interfaccia.
5) Per quanto riguarda il confronto col C++, che conosco bene, a mio avviso
cambia lievemente la sintassi, ma con l'uso di interfacce e classi astratte
si puo' tradurre TUTTO da java a c++ e viceversa.
   cya Nicola e buona giornata


 questione!!
Ovviamente l'oggetto Connection è il meno indicato per rappresentare il 
problema!

 Andrea
Infatti hai ragione è l'oggetto non sto descrivendo il linguaggio Java come 
limitato.
Non l'ho mai detto!