[DBIx::Class] MaLib::Schema::Result::Livre a besoin de la lib globale

12 réponses
Avatar
luc2
salut,

c'est djuuuuuuuuuur la conception !!!!!!!! oh la la... qu'est-c' qu'i'
faut et'e con pour vouloir en faire....

supposons que je veuille faire une lib globale qui gere plein de trucs,
qui manipule des entites, disons des livres pour faire concret.
j'instancie ma lib, je l'initialise, et c'est parti mon kiki :

my $lib = MaLib->new();
$lib->init();

mettons que je fasse une methode pour recuperer les livres :

sub get_livres
{
my $self = shift;
my $schema = $self->{_schema};
my @livres = $schema->resultset('Livre')->all;
return @livres;
}

je veux pouvoir recuperer des informations sur un livre :

my @livres = $lib->get_livres;
my $livre = shift @livres;
my $informations = $livre->get_informations;

je vais maintenant coder la methode "get_informations" :

vim lib/MaLib/Schema/Result/Livre.pm
sub get_informations
{
...
return $lib->get_informations( $self );
}

probleme : j'ai besoin d'une reference vers la $lib globale, et je n'ai
pas cette reference. pourquoi j'ai besoin de la $lib globale ? parce
qu'elle a initialise un element dont j'ai besoin pour recuperer ces
informations.

question : comment transmettre cette reference $lib a cet objet ?

10 réponses

1 2
Avatar
Paul Gaborit
À (at) 14 Jun 2013 10:17:45 GMT,
luc2 écrivait (wrote):

c'est djuuuuuuuuuur la conception !!!!!!!! oh la la... qu'est-c' qu'i'
faut et'e con pour vouloir en faire....

supposons que je veuille faire une lib globale qui gere plein de trucs,
qui manipule des entites, disons des livres pour faire concret.
j'instancie ma lib, je l'initialise, et c'est parti mon kiki :

my $lib = MaLib->new();
$lib->init();

mettons que je fasse une methode pour recuperer les livres :

sub get_livres
{
my $self = shift;
my $schema = $self->{_schema};
my @livres = $schema->resultset('Livre')->all;
return @livres;
}

je veux pouvoir recuperer des informations sur un livre :

my @livres = $lib->get_livres;
my $livre = shift @livres;
my $informations = $livre->get_informations;

je vais maintenant coder la methode "get_informations" :

vim lib/MaLib/Schema/Result/Livre.pm
sub get_informations
{
...
return $lib->get_informations( $self );
}

probleme : j'ai besoin d'une reference vers la $lib globale, et je n'ai
pas cette reference. pourquoi j'ai besoin de la $lib globale ? parce
qu'elle a initialise un element dont j'ai besoin pour recuperer ces
informations.

question : comment transmettre cette reference $lib a cet objet ?



Solution la plus directe : il suffit d'ajouter un paramètre à la méthode
get_informations.

Solution plus propre : manifestement la conception n'a pas été très bien
faite car il ne devrait pas y avoir ce type de besoins. Donç il faut
revoir la conception... mais il faudrait plus d'informations sur
l'application.



--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Perl en français - <http://perl.mines-albi.fr/>
Avatar
luc2
Le 14-06-2013, Paul Gaborit a écrit:

c'est djuuuuuuuuuur la conception !!!!!!!! oh la la... qu'est-c' qu'i'
faut et'e con pour vouloir en faire....

supposons que je veuille faire une lib globale qui gere plein de trucs,
qui manipule des entites, disons des livres pour faire concret.
j'instancie ma lib, je l'initialise, et c'est parti mon kiki :

my $lib = MaLib->new();
$lib->init();

mettons que je fasse une methode pour recuperer les livres :

sub get_livres
{
my $self = shift;
my $schema = $self->{_schema};
my @livres = $schema->resultset('Livre')->all;
return @livres;
}

je veux pouvoir recuperer des informations sur un livre :

my @livres = $lib->get_livres;
my $livre = shift @livres;
my $informations = $livre->get_informations;

je vais maintenant coder la methode "get_informations" :

vim lib/MaLib/Schema/Result/Livre.pm
sub get_informations
{
...
return $lib->get_informations( $self );
}

probleme : j'ai besoin d'une reference vers la $lib globale, et je n'ai
pas cette reference. pourquoi j'ai besoin de la $lib globale ? parce
qu'elle a initialise un element dont j'ai besoin pour recuperer ces
informations.

question : comment transmettre cette reference $lib a cet objet ?



Solution la plus directe : il suffit d'ajouter un paramètre à la méthode
get_informations.

Solution plus propre : manifestement la conception n'a pas été très bien
faite car il ne devrait pas y avoir ce type de besoins. Donç il faut
revoir la conception... mais il faudrait plus d'informations sur
l'application.



ca fait plusieurs fois qu'on me dit que la conception est mal faite. je
suis pret a le croire, je suis pret a modifier la conception, mais je ne
comprends pas en quoi elle est mal faite, j'ai pas l'impression d'avoir
fait des trucs trop exotiques.

je me suis relu plusieurs fois, le seul point un peu exotique, c'est le
fait d'avoir besoin de la reference vers la lib globale. je vais donc
donner davantage de details a ce sujet pour fixer les idees :

dans la methode "get_informations", les informations sont recuperees a
travers une "rabbitmq" qui a ete initialisee par $lib->init(); voila
pourquoi la methode a besoin de $lib.
Avatar
Paul Gaborit
À (at) 14 Jun 2013 13:20:05 GMT,
luc2 écrivait (wrote):

Le 14-06-2013, Paul Gaborit a écrit:

c'est djuuuuuuuuuur la conception !!!!!!!! oh la la... qu'est-c' qu'i'
faut et'e con pour vouloir en faire....

supposons que je veuille faire une lib globale qui gere plein de trucs,
qui manipule des entites, disons des livres pour faire concret.
j'instancie ma lib, je l'initialise, et c'est parti mon kiki :

my $lib = MaLib->new();
$lib->init();

mettons que je fasse une methode pour recuperer les livres :

sub get_livres
{
my $self = shift;
my $schema = $self->{_schema};
my @livres = $schema->resultset('Livre')->all;
return @livres;
}

je veux pouvoir recuperer des informations sur un livre :

my @livres = $lib->get_livres;
my $livre = shift @livres;
my $informations = $livre->get_informations;

je vais maintenant coder la methode "get_informations" :

vim lib/MaLib/Schema/Result/Livre.pm
sub get_informations
{
...
return $lib->get_informations( $self );
}

probleme : j'ai besoin d'une reference vers la $lib globale, et je n'ai
pas cette reference. pourquoi j'ai besoin de la $lib globale ? parce
qu'elle a initialise un element dont j'ai besoin pour recuperer ces
informations.

question : comment transmettre cette reference $lib a cet objet ?



Solution la plus directe : il suffit d'ajouter un paramètre à la méthode
get_informations.

Solution plus propre : manifestement la conception n'a pas été très bien
faite car il ne devrait pas y avoir ce type de besoins. Donç il faut
revoir la conception... mais il faudrait plus d'informations sur
l'application.



ca fait plusieurs fois qu'on me dit que la conception est mal faite. je
suis pret a le croire, je suis pret a modifier la conception, mais je ne
comprends pas en quoi elle est mal faite, j'ai pas l'impression d'avoir
fait des trucs trop exotiques.

je me suis relu plusieurs fois, le seul point un peu exotique, c'est le
fait d'avoir besoin de la reference vers la lib globale. je vais donc
donner davantage de details a ce sujet pour fixer les idees :

dans la methode "get_informations", les informations sont recuperees a
travers une "rabbitmq" qui a ete initialisee par $lib->init(); voila
pourquoi la methode a besoin de $lib.



Donc la méthode "get_informations" ne semble pas liée à un livre mais à
cette "rabbitmq" (même si elle utilise aussi un livre).

L'appel "$lib->get_informations($livre)" ne serait-il pas plus cohérent ?

--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Perl en français - <http://perl.mines-albi.fr/>
Avatar
Paul Gaborit
À (at) Sat, 15 Jun 2013 08:50:41 +0200,
Paul Gaborit écrivait (wrote):

Donc la méthode "get_informations" ne semble pas liée à un livre mais à
cette "rabbitmq" (même si elle utilise aussi un livre).

L'appel "$lib->get_informations($livre)" ne serait-il pas plus cohérent ?



Une bonne question à se poser est : qui fournit l'information ? Est-ce
le livre ou la "rabbitmq" ? Ici, il semble bien que c'est la "rabbitmq"
(donc $lib). D'où l'appel proposé ci-dessus.

--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Perl en français - <http://perl.mines-albi.fr/>
Avatar
luc2
Le 15-06-2013, Paul Gaborit a écrit:

Donc la méthode "get_informations" ne semble pas liée à un livre mais à
cette "rabbitmq" (même si elle utilise aussi un livre).

L'appel "$lib->get_informations($livre)" ne serait-il pas plus cohérent ?



Une bonne question à se poser est : qui fournit l'information ? Est-ce
le livre ou la "rabbitmq" ? Ici, il semble bien que c'est la "rabbitmq"
(donc $lib). D'où l'appel proposé ci-dessus.



c'est ce qu'on m'a deja dit, mais je reste perplexe.

voici les deux questions que tu t'es posees :

1) a qui la methode est-elle concretement liee ? $lib possede une
reference vers la "rabbitmq", et c'est $lib qui l'a initialisee. $lib
est donc la reponse a cette question.

2) qui fournit l'information ? reponse : $lib.

je vais faire une petite nuance a la question 1) : concretement, la
methode est liee a $lib, mais logiquement, on comprend tres bien qu'elle
soit liee a un livre etant donne qu'elle sert a recuperer des
informations sur un livre.

ensuite, au sujet des deux questions, je me demande si ce sont les
bonnes questions a se poser, s'il n'y en a pas d'autres, j'ai des doutes
sur le fait que ces criteres soient suffisants pour dire que c'est la
bonne facon de concevoir cette lib, j'ai l'impression qu'il en manque,
et je vais dire pourquoi :

prenons pour modele DBIx::Class : $schema permet de recuperer des
instances de $livre. pourtant, $livre possede une reference vers $schema
pour avoir une connexion a la base de donnees, et faire des requetes. ce
que je fais n'a donc rien de scandaleux, c'est pareil que ce que fait
DBIx::Class :

$schema instancie $livre qui possede une reference vers $schema
$lib instancie $livre qui possede une reference vers $lib

...et je pense que les auteurs de DBIx::Class sont meilleurs que moi en
conception, alors s'ils le font, c'est normal que je me pose des
questions quand on me dit que c'est mal.

second point qui me derange : mon idee est simple et me semble bonne, il
s'agit de pouvoir recuperer des informations sur un livre en faisant :

my $informations = $livre->get_informations;

c'est tellement naturel a faire que ce m'etonne fortement que l'on me
dise que c'est une erreur de conception. d'ailleurs, si on tente a
nouveau de prendre exemple sur DBIx::Class, on peut voir le meme genre
de principe pour recuperer une information :

my $contenu_du_champ_nom = $livre->nom;

si mes principes de conception sont mauvais, ceux de DBIx::Class le
seraient-ils aussi ?

---

en somme, c'est un probleme classique de conception auquel je suis
souvent confronte, pas forcement en rapport avec DBIx::Class : une lib
qui gere des entites qui ont besoin de quelque chose qui a ete
initialise dans la lib, et a chaque fois, on me traite de gland parce
que je file une reference de la lib aux entites...

quand on fouille sur le net, on trouve des trucs abstraits comme les
design patterns, des exemples de conception objet avec des animaux, mais
rien qui permette de trancher a coup sur en disant "c'est comme ca qu'il
faut concevoir", alors que ce cas me semble pourtant classique, meme
DBIx::Class y a ete confronte...
Avatar
espie
In article <51bed869$0$2300$,
luc2 wrote:
en somme, c'est un probleme classique de conception auquel je suis
souvent confronte, pas forcement en rapport avec DBIx::Class : une lib
qui gere des entites qui ont besoin de quelque chose qui a ete
initialise dans la lib, et a chaque fois, on me traite de gland parce
que je file une reference de la lib aux entites...


quand on fouille sur le net, on trouve des trucs abstraits comme les
design patterns, des exemples de conception objet avec des animaux, mais
rien qui permette de trancher a coup sur en disant "c'est comme ca qu'il
faut concevoir", alors que ce cas me semble pourtant classique, meme
DBIx::Class y a ete confronte...



Les design patterns n'ont rien d'abstrait. Ce que tu vois la s'apparente
au design pattern "flyweight", un compromis assez classique :-)

Premiere question a se poser: tes objets sont-ils independants de la
connexion a la base de donnees ? pourrait-on les lire (ecrire) vers
une autre base. Dans un cadre tres general, ca pourrait etre le cas,
mais pas vraiment.

Donc tu as le choix:
- mettre une back-reference vers ta connexion dans tous les objets.
Ca bouffe de la place.

- decider que ca bouffe trop de place, et donc laisser la connexion
dehors. C'est ce que dit flyweight (enfin, pas exactement, mais c'est
proche).

Bref, entre
$lib->get_info($book); # 1
$book->get_info($lib); # 2

et $book->get_info # 3

pas tant de differences que ca.

Apres, faut savoir ce que ton book va avoir le droit de faire avec sa
reference sur la lib. La tentation peut etre grande d'heriter, histoire
de dire "oh ben, j'ai un $book, j'ai plus besoin de passer la $lib".

Ca *peut* marcher, mais c'est un poil casse-gueule quand meme.

De toutes facons, c'est les joies du multi-methodes, ou pas loin. Au final
pas trop evident de savoir de qui c'est la responsabilite de faire quoi.

La question qui se pose c'est, quel est le code le plus simple a implementer,
et ca donne quoi en perfs ?

Je ne suis pas sur que je prendrais DBIx::Class comme exemple. Pour moi,
c'est l'exemple-meme du monstre trop complexe et over-designe... A la
limite, je prefere tous les DBIx::*Rose de John Syracusa.
Avatar
Paul Gaborit
À (at) 17 Jun 2013 09:35:37 GMT,
luc2 écrivait (wrote):

voici les deux questions que tu t'es posees :

1) a qui la methode est-elle concretement liee ? $lib possede une
reference vers la "rabbitmq", et c'est $lib qui l'a initialisee. $lib
est donc la reponse a cette question.

2) qui fournit l'information ? reponse : $lib.

je vais faire une petite nuance a la question 1) : concretement, la
methode est liee a $lib, mais logiquement, on comprend tres bien qu'elle
soit liee a un livre etant donne qu'elle sert a recuperer des
informations sur un livre.



Dans ce cas, rien n'empêche de passer $lib comme paramètre à la méthode
get_informations() d'un $livre.

Ou de stocker dans $livre, une référence vers $lib pour que
get_informations() puisse l'interroger. (Dans ce dernier cas, ne pas
oublier que Perl ne sait pas détecter/détruire les références
circulaires. Il faut l'aider.)

Pour choisir parmi ces trois solutions (celle de mon premier message et
les deux solutions du message actuel), on peut tenir compte d'autres
informations : les informations recupérées sont-elles toujours les mêmes
pour un livre ou peuvent-elles varier selon la source ($lib) ?
Vont-elles varier au cours du temps ou non (ou ont-elles la même durée
de vie que l'objet livre ou non) ? etc.

--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Perl en français - <http://perl.mines-albi.fr/>
Avatar
luc2
Le 17-06-2013, Marc Espie a écrit:

en somme, c'est un probleme classique de conception auquel je suis
souvent confronte, pas forcement en rapport avec DBIx::Class : une lib
qui gere des entites qui ont besoin de quelque chose qui a ete
initialise dans la lib, et a chaque fois, on me traite de gland parce
que je file une reference de la lib aux entites...




quand on fouille sur le net, on trouve des trucs abstraits comme les
design patterns, des exemples de conception objet avec des animaux, mais
rien qui permette de trancher a coup sur en disant "c'est comme ca qu'il
faut concevoir", alors que ce cas me semble pourtant classique, meme
DBIx::Class y a ete confronte...



Les design patterns n'ont rien d'abstrait. Ce que tu vois la s'apparente
au design pattern "flyweight", un compromis assez classique :-)

Premiere question a se poser: tes objets sont-ils independants de la
connexion a la base de donnees ? pourrait-on les lire (ecrire) vers
une autre base. Dans un cadre tres general, ca pourrait etre le cas,
mais pas vraiment.

Donc tu as le choix:
- mettre une back-reference vers ta connexion dans tous les objets.
Ca bouffe de la place.

- decider que ca bouffe trop de place, et donc laisser la connexion
dehors. C'est ce que dit flyweight (enfin, pas exactement, mais c'est
proche).

Bref, entre
$lib->get_info($book); # 1
$book->get_info($lib); # 2

et $book->get_info # 3

pas tant de differences que ca.

Apres, faut savoir ce que ton book va avoir le droit de faire avec sa
reference sur la lib. La tentation peut etre grande d'heriter, histoire
de dire "oh ben, j'ai un $book, j'ai plus besoin de passer la $lib".

Ca *peut* marcher, mais c'est un poil casse-gueule quand meme.

De toutes facons, c'est les joies du multi-methodes, ou pas loin. Au final
pas trop evident de savoir de qui c'est la responsabilite de faire quoi.

La question qui se pose c'est, quel est le code le plus simple a implementer,
et ca donne quoi en perfs ?

Je ne suis pas sur que je prendrais DBIx::Class comme exemple. Pour moi,
c'est l'exemple-meme du monstre trop complexe et over-designe... A la
limite, je prefere tous les DBIx::*Rose de John Syracusa.



dans ta reponse, tu as cite d'autres criteres a prendre en compte,
d'autres questions a se poser. cela confirme ce que je pensais : j'ai
raison de douter, et de me poser des questions. cependant, je ne suis
pas satisfait d'avoir raison; je trouve que c'est un cas qui est
tellement classique, qu'il ne devrait y avoir qu'une seule solution.

j'ai pris DBIx::Class pour exemple, mais n'importe quelle lib qui
manipule des entites sont confrontees au probleme, et ils le gerent de
la meme facon que moi : en filant une reference de la lib globale a
l'entite.

pour l'instant, j'ai suivi le critere "facile a implementer", et j'ai
fait ceci :

vim Schema.pm
_PACKAGE__->mk_group_accessors('simple' => qw/lib/);

vim MaLib.pm
$schema->lib( $self );

vim Schema/Result/Book.pm
my $lib = $self->result_source->schema->lib;

on pourrait critiquer : $schema ne sert plus seulement a requeter la
base de donnees, elle est maintenant liee a ma $lib car elle en possede
une reference. d'un autre cote, c'etait pas possible de separer les
deux, car j'avais deja impose ce lien en m'entetant a vouloir une
methode "get_informations" dans Book.pm.
Avatar
luc2
Le 17-06-2013, Paul Gaborit a écrit:

voici les deux questions que tu t'es posees :

1) a qui la methode est-elle concretement liee ? $lib possede une
reference vers la "rabbitmq", et c'est $lib qui l'a initialisee. $lib
est donc la reponse a cette question.

2) qui fournit l'information ? reponse : $lib.

je vais faire une petite nuance a la question 1) : concretement, la
methode est liee a $lib, mais logiquement, on comprend tres bien qu'elle
soit liee a un livre etant donne qu'elle sert a recuperer des
informations sur un livre.



Dans ce cas, rien n'empêche de passer $lib comme paramètre à la méthode
get_informations() d'un $livre.



si je choisis cette solution, voici comment se presenterait le code du
point de vue de l'utilisateur de la lib :

my $lib = MaLib->new();
my @livres = $lib->get_livres;
my $livre = shift @livres;
my $informations = $livre->get_informations($lib);

du point de vue de l'utilisateur de la lib, je me demanderais pourquoi
je dois filer $lib en parametre. cela signifierait que je sais que la
methode a besoin de quelque chose qui est contenu dans $lib, que je sais
qu'il y a une "rabbitmq" la-dessous, que je connais l'implementation de
"get_informations". cela contredit le principe de la boite noire selon
lequel on a une lib, on ne sait pas ce qu'il y a dedans, et on l'utilise
rien sans comprendre.

Ou de stocker dans $livre, une référence vers $lib pour que
get_informations() puisse l'interroger. (Dans ce dernier cas, ne pas
oublier que Perl ne sait pas détecter/détruire les références
circulaires. Il faut l'aider.)

Pour choisir parmi ces trois solutions (celle de mon premier message et
les deux solutions du message actuel), on peut tenir compte d'autres
informations : les informations recupérées sont-elles toujours les mêmes
pour un livre ou peuvent-elles varier selon la source ($lib) ?



je ne suis pas sur de comprendre la question.

si j'instancie 2 MaLib avec les memes parametres, j'aurai 2 instances
identiques de MaLib : $lib1 et $lib2. si je recupere les informations
sur un livre avec ces libs, j'obtiendrai la meme reponse, donc, oui, les
informations recuperees sont toujours les memes pour un livre.

si j'instancie 2 MaLib avec des parametres differents, j'aurai 2
instances differentes de MaLib : $lib3 et $lib4. si je recupere les
informations sur un livre avec ces libs, j'obtiendrai des reponses
differentes : dans l'un des cas j'aurais des infos, dans l'autre
j'aurais "Livre inconnu" (un livre n'est repertorie que dans un seul
MaLib). donc, oui, les informations peuvent varier selon la lib.

dans l'immediat, je n'ai qu'une seule instance de MaLib a considerer.

Vont-elles varier au cours du temps ou non (ou ont-elles la même durée
de vie que l'objet livre ou non) ?



les informations peuvent varier au cours du temps.

etc.
Avatar
Paul Gaborit
À (at) 17 Jun 2013 15:29:12 GMT,
luc2 écrivait (wrote):

si j'instancie 2 MaLib avec des parametres differents, j'aurai 2
instances differentes de MaLib : $lib3 et $lib4. si je recupere les
informations sur un livre avec ces libs, j'obtiendrai des reponses
differentes : dans l'un des cas j'aurais des infos, dans l'autre
j'aurais "Livre inconnu" (un livre n'est repertorie que dans un seul
MaLib). donc, oui, les informations peuvent varier selon la lib.



Donc un $livre n'existe que dans une seule $lib. Et la durée de vie d'un
$livre est strictement inférieure à la durée de vie de la $lib dont il
provient. Donc la solution consiste (un peu comme le fait DBIx::Class) à
stocker une référence à $lib dans chacun des $livre... sans oublier de
les détruire juste avant la disparation de $lib (ou alors en utilisant
les weakref mais c'est plus sioux).

Vont-elles varier au cours du temps ou non (ou ont-elles la même durée
de vie que l'objet livre ou non) ?



les informations peuvent varier au cours du temps.



En conception objet, c'est surtout les durées de vie qu'il faut comparer
(d'où la question).

--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Perl en français - <http://perl.mines-albi.fr/>
1 2