Newsgroup: it.comp.lang.perl
Titolo: OO-perl: composizione e aggregazione

Salve a tutti,
chi mi sa dire come implementare le relazioni di composizione e di 
aggregazione in Perl?
#####################################esempio1
# 
main.pl
...
use Obj1;
use Obj2;
my $var1 = Obj1->new;
my $var2 = Obj2->new;
# voglio che Obj2::var2 abbia come componente Obj1::var1
# ma questi vive anche al di fuori di Obj2
$var2->setComponente(\$var1);
#Obj2.pl
...
sub new {
	my $class = shift;
	my $self = {}
	$self->{COMPONENTE} = undef;
	bless $self, $class;
	return $self;
}
sub setComponente
{
	my $self = shift;
	$self->{COMPONENTE} = shift;
}
sub Foo {
	my $self = shift;
	#se provo ad utilizzare "$self->{COMPONENTE}" ricevo errore!
}
#####################################esempio2
# 
main.pl
...
use Obj2;
my $var2 = Obj2->new;
# voglio che Obj2::var2 abbia come componente Obj1::var1
# ma questi vive solo all'interno di Obj2
#Obj2.pl
...
use Obj1;
sub new {
	my $class = shift;
	my $self = {}
	$self->{COMPONENTE} = Obj1->new;
	bless $self, $class;
	return $self;
}
sub Foo {
	my $self = shift;
	#se provo ad utilizzare "$self->{COMPONENTE}" ricevo errore!
}
#####################################FINE
E' evidente che non ho capito qualcosa! Grazie

 }
Questo dovrebbe andare, invece. Sicuro di averlo copiato giusto?
-- 
  dakkar
  sono su PerlMonks.org, 
use.perl.org
, PAUSE/CPAN

 }
Infine, cerca di evitare il mixed-case per i nomi dei metodi, perché in 
Perl non si usa (per i nomi composti utilizza l'underscore). In Perl si 
usa il mixed-case solo per i nomi delle classi/package, e in questo caso 
comunque non si usa mai la lettera iniziale minuscola (arguisco che 
provieni da Java o da qualche linguaggio Microsoft ;-)
Tutorial sulla OOP in Perl...
http://perldoc.perl.org/perltoot.html
... Con tanto di paragrafo sull'aggregazione di oggetti ;-)
http://perldoc.perl.org/perltoot.html#Aggregation
Moduli CPAN che automatizzano varie tecniche di aggregazione:
http://search.cpan.org/~dconway/Class-Delegation-1.06/
http://search.cpan.org/~dwheeler/Class-Delegator-0.04/
http://search.cpan.org/~simon/Class-HasA-1.00/
http://search.cpan.org/~kstar/Class-Delegate-0.06/Delegate.pm
Ciao,
Emanuele.

Innanzitutto, grazie per le vostre risposte.
Corretti alcuni errori, i codici funzionano perfettamente. Ma il 
problema e' risolto solo a meta'!
Segue il codice di un programma che apre una piccola finestra per il 
calcolo del quadrato di un intero.
Il calcolo vero e proprio e' rimandato alla classe Quadrato, derivata da 
Funzione (calcola il quadrato e ritorna una stringa con il risultato o 
con l'errore).
Nella classe 'gui' ho marcato con il commento '# trucco per farlo 
funzionare' le istruzioni che ho utilizzato per arrivare comunque ad un 
risultato.
Il problema e' che il reference a Quadrato in gui::new esiste e 
funziona, mentre nella sub gui::on_calcola non e' piu' valido.
In allegato c'e' il file glade. Per far funzionare il programma bisogna 
eliminare le righe in cui e' presente $self->{CALCOLATORE}
e de-commentare quelle con $calcolatore.
Grazie
#############################################Funzione.pm -> tutto OK!
package Funzione;
use strict;
use warnings;
my $ERR_GEN;
my $ERR_LIM;
# inizializza ed esporta le variabili $ERR_GEN e $ERR_LIM
sub BEGIN
{
	use Exporter();
	@Funzione::ISA = qw(Exporter);
	@Funzione::EXPORT = qw($ERR_GEN $ERR_LIM);
	$Funzione::ERR_GEN = 1000;
	$Funzione::ERR_LIM = 1001;
}
# inizializza la variabile privata $ERR_CODE
sub new()
{
	my $class = shift;
	my $self = {};
	$self->{ERR_CODE} = 0;
	bless $self, $class;
	return $self;
}
# ritorna stringa con messaggio di errore in base al valore di $ERR_CODE
sub Errore()
{
	my $self = shift;
	my $output = "";
	if ($self->{ERR_CODE} == $Funzione::ERR_GEN) {
		$output = "Errore generico\n";
	} elsif ($self->{ERR_CODE} == $Funzione::ERR_LIM) {
		$output = "Errore: fuori limiti\n";
	}
	return $output;
}
return 1;
###############################################Quadrato.pm -> tutto OK!
package Quadrato;
use strict;
use warnings;
use Funzione;
# variabile pubblica
my $xmax;
sub BEGIN {
	use Exporter();
	@Quadrato::ISA = qw(Funzione Exporter);
	@Quadrato::EXPORT = qw($xmax);
	$Quadrato::xmax = 100;
}
sub new()
{
	my $class = shift;
	my $arg = shift;
	my $self = $class->SUPER::new;
	$self->{X} = defined $arg ? $arg : 0;
	$self->{QUADX} = -1;
	bless $self, $class;
	return $self;
}
sub setX()
{
	my $self = shift;
	my $arg = shift;
	$self->{X} = $arg;
}
sub Calcola()
{
	my $self = shift;
	if ($self->{X} >= -$Quadrato::xmax && $self->{X} <= 	$Quadrato::xmax) {
		$self->{QUADX} = $self->{X}*$self->{X};
	} else {
		$self->{ERR_CODE} = $Funzione::ERR_LIM;
	}
}
sub Risultato()
{
	my $self = shift;
	my $output = "";
	if (!$self->{ERR_CODE}) {
		$output = "Il quadrato di $self->{X} e' $self->{QUADX}.";
	} else {
		$output = Errore($self);
	}
	return $output;
}
sub Errore()
{
	my $self = shift;
	my $output = $self->SUPER::Errore() . "Devi inserire un intero\n".
				"compreso tra -$Quadrato::xmax e $Quadrato::xmax.";
	$self->{ERR_CODE} = 0;
	return $output;
}
return 1;
###########################################
gui.pm
 -> ERRORE!!!
package gui;
use strict;
use warnings;
# use Quadrato;	    # trucco per farlo funzionare
my $glade_xml;
# my $calcolatore;  # trucco per farlo funzionare
sub BEGIN
{
	use Gtk '-init';
	use Gtk::GladeXML;
	Gtk::GladeXML->init ();
}
sub new
{
	my $class = shift;
	$glade_xml = Gtk::GladeXML->new("gui.glade");
	$glade_xml->signal_autoconnect_from_package('gui');
 
	# $calcolatore = Quadrato->new; # trucco per farlo funzionare
	my $self = {};
	$self-{CALCOLATORE} = shift;    # tentativo che fallira'
	bless $self, $class;
	return $self;
}
sub on_calcola
{
	my $self = shift;
	my $entry = $glade_xml->get_widget("entry1");
	my $strInput = $entry->get_text();
	if ($strInput =~ m/^[ \t]*[-+]?[ \t]*\d+[ \t]*$/) {
		my $num = int($strInput);
		# $calcolatore->setX($num); # trucco per farlo funzionare
		# la seguente fallisce
		# dice che il valore e' non inizializzato!
		# nella sub new, invece e' tutto ok
		$self-{CALCOLATORE}->setX($num);
		# $calcolatore->Calcola(); # trucco per farlo funzionare
		my $label = $glade_xml->get_widget("label1");
		# $label->set_text($calcolatore->Risultato()); # trucco per farlo 
funzionare
		$entry->select_region(0,length($strInput));
	} else {
		$entry->set_text("");
	}
	$entry->grab_focus();
}
sub on_button1_clicked
{
	my $self = shift;
	on_calcola($self);
}
sub on_entry1_activate
{
	my $self = shift;
	on_calcola($self);
}
sub gtk_main_quit
{
	Gtk->main_quit;
}
return 1;
################################################
main.pl
#!/usr/bin/env perl
use strict;
use warnings;
use gui;
use Quadrato; 			# tentativo
# creo un calcolatore
my $q = Quadrato->new(); 	# tentativo
# crea la finestra
#my $window = gui->new();	# versione funzionante
my $window = gui->new($q);	# tentativo
# main loop
Gtk->main();
exit 0;

 my $window = gui->new($q);	# tentativo
Deve funzionare. Cfr. quanto detto sopra.


 Come prima. $xmax e` di classe, non di istanza.
e quindi?

 a $ERR_CODE? E a cosa mi sto riferendo?
Esempio:
 package Coso;
 {
  my $var=5;
  $Coso::var=7;
  sub get_my_var {
   return $var;
  }
 } # questo blocco delimita la visibilita` della my $var
 package main;
 print Coso::get_my_var(),"\n";
 print $Coso::var,"\n";
Le variabili lessicali (dichiarate con 'my') hanne un namespace
diverso da quelle di package (Coso::var).
Per i dettagli, guarda la manpage perlmod


 
eppure l'ho letta. comunque la rileggero' con piu' attenzione
ciao

 eppure l'ho letta. comunque la rileggero' con piu' attenzione
Come ti diceva Dakkar, le variabili lessicali (cioè quelle dichiarate 
con il modificatore "my") e le variabili di package (o globali) vivono 
in namespace completamente ortogonali, quindi anche se hanno lo stesso 
nome si riferiscono ad oggetti del tutto diversi e decorrelati.
Dai un'occhiata a quest'articolo, che sviscera l'argomento:
http://www.perl.it//documenti/articoli/namespaces/index.html
Nel frattempo osserva quest'esempio, che dovrebbe già dissipare molti 
dubbi (se non tutti).
Ci metto anche il modificatore "our", che permette di dichiarare una 
variabile _globale_ localizzandone il *nome* (ma non il valore!) nello 
scope nel quale è dichiarata.
	use strict;
	my $var1 = 'lessicale';
	package Package1;
	{
		our $var1 = 'GLOBALE';
		print "1: $var1\n";
		$Package1::var2 = 'ALTRA GLOBALE';
	}
	my $var2 = 'altra lessicale';
	print "2: $var1\n";
	print "3: $Package1::var1\n";
	print "4: $var2\n";
	print "5: $Package1::var2\n";
La stampa prodotta è la seguente:
1: GLOBALE
2: lessicale
3: GLOBALE
4: altra lessicale
5: ALTRA GLOBALE
Ciao,
Emanuele.

Grazie.
E' tutto molto piu' chiaro (almeno il meccanismo, il perche' sia cosi' 
e' un altro paio di maniche :-)) )
ciao

 Una stringa si puo' anche solo stampare con print. Tu come avresti fatto?
La classe che fa i calcoli si limita a fare i calcoli (Modello)
Una classe che disegna la GUI si limita a disegnare la GUI (Vista) (in
questo caso sono gli widget Gtk2 + libglade)
La classe che incolla i due si preoccupa di convertire i dati di
modello in dati di vista (Controller).
Ovvero:
- Funzione fa da numeri a numeri
- Gtk2::Label mostra una stringa
- LaTuaClasse prende i valori dall'utente, li passa a Funzione,
  riprende i numeri, ne fa una stringa e la passa alla Label


 e' un altro paio di maniche :-)) )
Historical Reasons, la scusa standard nel mondo Unix ;-)
Inizialmente Perl aveva solo variabili globali.
Poi e` stato introdotto 'local' (studiatelo, non usarlo se non nei 2-3
casi in cui e` indispensabile, ma ormai non si usa praticamente piu`)
che fa cosa un po' strane.
Infine sono state introdotte le variabili lessicali (e i blocchi di
visibilita`, e le chiusure, e i pad, etc etc)
Ma ovviamente la roba vecchia non si puo` togliere ;-)


Siccome siamo OT (almeno credo), se vuoi puoi scrivermi sulla mia e-mail 
   (ricordati di fare 's/NOSPAM//')
In ogni caso secondo me la situazione e' la seguente:
la classe gui non e' un elemento di Gtk, ma quello che tu chiami 
controllo. questo perche' accedo agli elementi Gtk tramite Glade.
Quindi dovrebbe essere lei a creare la stringa una volta ottenuti i dati 
da Quadrato. Sicuramente hai ragione sul fatto che Quadrato dovrebbe 
avere una sub get per ritornare il valore calcolato, ma questo non 
impedisce che abbia una funzione tipo toString() per ritornare il 
risultato come stringa.
ciao

 come stringa.
E qui andiamo sul filosofico ;-)
Io preferisco evitare che classi di modello (quelle cha fanno davvero
le cose) abbiano comportamenti dipendenti da scelte di vista (es. la
lingua in cui mostrare i risultati). Di solito mi faccio domande del
tipo: "se usassi una CLI invece di una GUI, dovrei cambiare il
modello?", "se invece che in Italiano dovessi uscire in Tedesco?", "e
per gli utenti ciechi?"
