Newsgroup: it.comp.lang.c++
Titolo: Classe: UTILITY; Modificando un metodo private mi ricompila tutto il progetto!!

Ci sono oggetti che servono quasi dappertutto, per es. quelli che gestiscono
le stringhe, le date, ecc. nel nostro caso consideriamo una classe "UTILITY"
che viene considerata come un mattone che viene utilizzato da quasi tutte le
classi di un progetto.
Se ci accorgiamo che in questa classe "UTILITY" ci serve una nuova variabile
"private", essa mi comporta che tutte le classe che vedono la classe
"UTILITY" vengono ricompilate anche se tale modifica non dovrebbe avere
influenze esterne.
Ho letto che è possibile evitare la ricompilazione di tutto il progetto
utilizzando le doppie interfacce!
Sarebbe a dire che la classe quando viene ricompilata richiama il suo file
UTILITY.H mentre le altre classi richiamano "UTILITY2.H" in cui ci sono
solo: i metodi e le variabili "public".
Ho provato a fare una cosa del genere, (forse sbaglio in qualcosa), ma
ottengo degli errori perché vengono considerati come due oggetti diversi.
Chi mi vuole spiegare come applicare questa tecnica delle doppie interfacce,
oppure se c'è un altro metodo per evitare le compilazioni globali del
progetto quando viene modificato un metodo o una variabile "private".
Grazie a tutti quelli che vorranno intervenire.
Ciao
--
Alessandro Pulvirenti
ale...@cedss.it

Sempre per il solito problema di parti comuni...
le strutture dati semplici che utilizzate in molte parti del progetto, li
inserite in un solo .H (o in alcuni .H) e ogni volta che avete una nuova
struttura base la inserite in questo files ?
Il problema che la modifica di un files che viene visto da molte classi ne
consegue una ricompilazione di tutte le classi.
E' meglio centralizzare le strutture base in pochi files .H oppure cercare
di mettere quanto meno strutture dati in un unico files in modo che vengano
viste solo ed esclusivamente dove vengono utilizzate ?
Sempre a un problema ma niente... ma non lo č!
Grazie a chi interverrŕ


interfacce,
Un'idea che mi viene al volo potrebbe essere quella di "ingannare" il
compilatore fin dalla prima stesura della dichiarazione della classe,
inserendo una variabile membro abbastanza grande da contenere tutte le
variabili private presenti e future.
// UTILITY2.H - usato solo all'esterno, da chi utilizza la classe
class UTILITY
{
    public:
        int x;
        void Stampa();
    private:
        unsigned char BUFFER[100];
};
// UTILITY.H - usata per implementare la classe
class UTILITY
{
    public:
        int x;
        void Stampa();
    private:
        int m1;
        char m2[10];
        unsigned char BUFFER[88];    // La differenza tra 100 e lo spazio
occupato da m1 e m2
};
Se serve aggiungere qualche variabile membro si agisce solo su UTILITY.H.
Certo non e' molto elegante, ne' efficiente perche' si spreca un po' di
spazio.
----
A presto,
Angelo.

 struttura base la inserite in questo files ?
No. Non ha senso creare dipendenze inutili. Perche quando creo una nuova 
classe dovrei ricompilare tutto, anche i progetti che non la usano?
Conviene usare un header per ogni classe, e mettere piu' classi in un unico
file solo quando sono classi che non si usano separatamente (es. un contenitore
e un iteratore per accedervi).
	Mauro

 influenze esterne.
E' possibile isolare l'implementazione di una classe ed evitare di dover
ricompilare tutto ad ogni modifica.
Tutti i dati e i metodi privati della classe vanno messi in una struttura ausiliaria
definita nel file .c che implementa la classe. L'unica cosa che rimane nella
parte privata della classe originale e' un puntatore alla struttura ausiliaria.
In questo modo si puo' modificare a piacere l'implementazione e aggiungere 
variabili private senza ricompilare altre classi.
//---------------
// utility.h
class Utility {
  class Implementation;
  Implementation * _impl;
public:
  Utility( ... );
  ~Utility();
  ...
};
//--------------
// utility.c
#include "utility.h"
struct Utility::Implmentation {
  // parte privata di Utility
};
Utility::Utility( ... ) 
  : _impl( new Implementation )
{
  ...
}
Utility::~Utility()
{
  ...
  delete _impl;
}
...
//--------------
Per accedere ai membri privati:
prima: _x, _f1()
dopo: _impl->x, _impl->f1()
Il copy constructor e copy assignment di default generati dal compilatore
non vanno piu' bene: copiano solo il puntatore. Si devono quindi disabilitare o
reimplementare.
Invece di distruggere esplicitamente _impl nel distruttore, si puo' usare
un auto_ptr<>; in questo caso le copie devono essere disabilitate.
Svantaggi: Inefficienza
- non si possono usare funzioni inline
- per creare un'istanza della classe bisogna allocare memoria dinamica.
Questo e' un problema per piccoli oggetti che vengo creati e distrutti spesso o
restutuiti per valore dalle funzioni. Di solito pero' le piccole classi
diventano stabili in breve tempo e quindi hanno meno bisogno di isolare
l'implementazione.
Per altre informazioni:
Guru of the Week #28: 
http://www.cntc.com/resources/gotw028.html
J. Lakos: "Large-Scale C++ Software Design"  Addison-Wesley
	Mauro

viste solo ed esclusivamente dove vengono utilizzate ?
Penso che il problema sia connesso a come hai scelto di
dividere il tuo progetto in "componenti" (molti di essi, si spera,
_riusabili_), o "sottosistemi" che dir si voglia.  Il componente
e` "l'unita` di riusabilita`" (Lakos).  Martin tende a chiamarli
"categorie", ma e` sempre lo stesso concetto -- una piccola
collezione di classi, sviluppate e manutenute assieme, che 
collaborano per offrire un qualche specifico, ben definito
insieme di funzionalita`.
Ogni componente puo` essere associato con uno o piu` file
di header "interni", inclusi da tutti e soli i sorgenti che lo
implementano (ma non ci sono particolari vantaggi ad
"atomizzare" questi header interni, tanto vale averne uno
per componente/categoria/sottosistema); e uno o piu` file
di header "esterni", inclusi dai sorgenti che lo usano, che
espongono la funzionalita` che il componente mette a
disposizione del resto del progetto.
La cosa piu` "comoda" e` forse di avere un solo header
"esterno" per componente, ma questo potrebbe portare
a una granularita` eccessivamente grossolana in termini
di controllo delle dipendenze.  La soluzione che adotto io
di solito: molteplici "veri" header esterni che "affettano" la
funzionalita` del componente nelle sue parti costitutive
che hanno un qualche senso indipendentemente le une
dalle altre (com'e` ad esempio, nella libreria standard C++,
la divisione fra <istream>, <ostream>, <ios>, <iomanip>,
ecc); un "header esterno di comodo", che include tutti gli
altri, per quegli utenti che preferiscono la comodita` alla
granularita` (soprattutto quando, nel corso degli anni, il
componente si e` ben stabilizzato).
Naturalmente, tutti gli header esterni sono protetti contro 
molteplici inclusioni, e sono autosufficienti nel senso di 
avere (il piu` possibili con dichiarazioni forward, ma, se 
serve, anche con #include interni agli header, ecc) tutto
quel che serve per essere inclusi "senza prerequisiti".
Alex

John Lakos "Large Scale C++ Software Design" consiglia di utilizzare, in
casi come questi di ricompilazione selvaggia, variabili statiche locali
al file .c al posto di variabili private nella definizione della classe
che provoca le molteplici dipendenze.
In questo modo l'interfaccia della classe non viene modificata qualora
sia necessario aggiungere nuove variabili alla classe stessa.
Comunque, uno dei principi basilari della programmazione OO e'
l'"Open-Close principle" che afferma: ogni classe deve essere (o modulo
o elemento del sistema) deve essere aperto alle estensioni, ma chiuso
alle modifiche. Quindi, dovresti strutturare il tuo programma in modo
che la classe Utility non venga piu' modificata (ogni modifica puo'
comportare la necessita' di eseguire nuovamente i test di qualita' su
tutte le classi/moduli che utilizzano la suddetta classe), ma al piu'
estesa mediante una nuova classe derivata dalla stessa che implementi le
nuove funzionalita'.
Se ti vedi costretto a modificare piu' volte una classe che viene
utilizzata da molte altre classi/moduli del programma allora e' meglio
che ti fermi e rivedi concettualmente (a livello di progetto) la
struttura del programma e la classe stessa, prima di intervenire sul
codice. Probabilmente, in questo modo riesci a ridurre (o meglio
eliminare) le successive necessita' di modifica e quindi riduci i
problemi di lunga ricompilazione del programma.
massimo

 che provoca le molteplici dipendenze.
Solo le variabili private _statiche_ possono essere sostituite con
variabili statiche locali al file .c
Nel caso delle variabili non-statiche c'e' una copia della variabile
in ogni istanza della classe e non si possono sostituire con una sola
variabile.
	Mauro


Chiaramente, hai ragione.
Dopo l'invio del messaggio sono stato inseguito per circa 15 minuti dal
libro di Lakos che aveva l'intenzione di schiantarsi sulla mia testa.
La dicitura corretta e': ...consiglia di utilizzare...variabili statiche
locali al file .c al posto di variabili *statiche* private nella
definizione della classe che provoca le molteplici dipendenze.
Nel caso il problema della ricompilazione sia legato all'introduzione in
fasi successive di nuove variabili o funzioni menbro private (stante la
considerazione che una classe non dovrebbe mai essere modificata, ma al
piu' estesa da un'altra classe che ne possa modificare o ampliare le
funzionalita') la possibile procedura, per incapsulare queste modifiche
in modo da ridurre il problema, dipende da come si manifesta la
dipendenza delle altre classi da quella incriminata (la chiamero' classe
Ovunque).
Se la dipendenza e' dovuta ad un elevato numero di classi derivate dalla
Ovunque, penso che una soluzione possibile (e necessaria, seppur
dolorosa) sia quella di eseguire un refactoring del programma, almeno
per la parte di classi in questione. Con il refactoring, possono essere
introdotte soluzioni derivate dai design pattern che favoriscono
l'utilizzo del object composition rispetto alla class inheritance.
Infatti, la object composition viene utilizzata mediante definizione
dinamica all'esecuzione del programma ed in questo modo permette di
mantenere le classi incapsulate, e di conseguenza riduce la dipendenza
delle classi client dalle classi server (che vengono definite negli
header delle classi client solo in nome).
Se la dipendenza e' dovuta all'utilizzo della classe Ovunque definita
come istanza negli header delle classi cliente, si puo' definire una
classe derivata dalla classe Ovunque, chiamata ad esempio OvunqueImp,
dove vengono trasferite tutte le funzionalita' della Ovunque. La Ovunque
viene trasformata in classe astratta (con opportuni accorgimenti diventa
una classe protocollo o interfaccia), e in questo modo tutte le
componenti private della OvunqueImp possono essere modificate senza
richiedere la ricompilazione dei client della classe Ovunque. Se la
classe client della Ovunque lo richiede la stessa Ovunque puo' essere
definita *in nome* nell'header della client , mentre le classi concrete
derivate dalla Ovunque vengono definite ed utilizzate solo entro il file
.c della classe client (la ri-compilazione interessa solo i singoli file
.c e non gli header).
Va rilevato che le classi derivate dalla OvunqueImp sono comunque
dipendenti da qualsiasi modifica effettuata sulla stessa OvunqueImp.
Quindi, anche qui va considerata l'opzione di utilizzare opportuni
design pattern in modo da ridurre tale dipendenza.
Comunque, il consiglio e', qualora si presentino questi problemi di
dipendenza selvaggia, di procurarsi alcuni libri dove sono descritti
alcuni approcci da utilizzare per migliorare la situazione. I libri
suddetti sono molti, ma tra essi si possono leggere:
E. Gamma & al. "Design Patterns Elements of Reusable Object-Oriented
Software" Addison-Wesley Professional Computing Series
J. Lakos "Large-Scale C++ Software Design" Addison-Wesley Professional
Computing Series
S. Meyers "Effective C++" Addison-Wesley
B. Martin "The separation of interface and implementation in C++"
Proceedings of the 1991 Usenix C++ conference.
M. Linton "Encapsulating a C++ library" Proceedings of the 1992 Usenix
C++ conference.