JeuWeb - Crée ton jeu par navigateur
[JS] Boucle for..in Array et ajout de methodes via prototype = conflit?! - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : [JS] Boucle for..in Array et ajout de methodes via prototype = conflit?! (/showthread.php?tid=7068)

Pages : 1 2 3 4 5 6 7 8 9 10 11


RE: [JS] Boucle for..in Array et ajout de methodes via prototype = conflit?! - Xenos - 02-08-2013

Citation :Et je coderais la même version en objet propre tu comprendras mieux.
J'attends donc ton point de vue, histoire que je sois moins con et que je comprenne enfin au lieu de m'entendre dire, sans plus d'explication, que "c'est de la merde" (Karadoc).

Citation :Je me tue à démontrer que c'est le but d'une bibliothèque
Là, ok, dans un tel cas, je suis d'accord avec toi. Et oui, l'idée d'utiliser simplement l'array [] pour un conteneur peut suffire (mais rien ne garantis alors que le conteneur contiendra uniquement un type d'objet donné, en tous cas, pour ce qui est de php).


RE: [JS] Boucle for..in Array et ajout de methodes via prototype = conflit?! - niahoo - 02-08-2013

Voici un équivalent de ton code en Erlang. Attention, c'est de l'objet et les objets en erlang sont des process concurrents (en somme, quand on instancie une biblio on l'a dans tout le système, ce n'est pas un objet qui va vivre seulement le temps d'une requête, c'est pour ça que le code peut paraître long pour ce qu'il fait. Ceci dit, pour le moment ton code PHP ne montre pas comment on traduirait tous les livres.

edit: la version colorée (maudit forum)

http://paste.bouh.org/f3859a4f06fc46bd9e2be95f3edbc070


-module(biblio).
-export([livre/2,biblio/0]).
-export([add_livre/2]).
-export([new/0]).
-export([test/0]).

-type livre() :: {livre, Titre :: string(), Texte :: string()}.
-type biblio() :: {biblio, Books :: [livre()]}.

livre(Titre,Texte) -> {livre, Titre, Texte}.

new() -> spawn(fun() -> biblio() end).

biblio() ->
biblio([]).

add_livre(Biblio,Book) ->
Biblio ! {add_book, Book, self()},
receive Response -> Response end.

get_livre(Biblio,Titre) ->
Biblio ! {get_livre, Titre, self()},
receive Response -> Response end.

%% Internal ------------------------

biblio(Books) ->
NewBooks =
receive
{get_livre, BookName, From} ->
Result = orddict:find(BookName,Books),
From ! Result,
orddict:erase(BookName,Books)
; {add_book, {livre, Titre,_}=NewBook, From} ->
NewDict = orddictConfusedtore(Titre, NewBook, Books),
From ! ok,
NewDict
; _ -> Books
end,
biblio(NewBooks).

test() ->
Bibli = new(),
add_livre(Bibli,livre("Hamlet", "One upon a time ...")),
io:format("Recup du bouquin : ~p~n",[get_livre(Bibli,"Hamlet")]),
io:format("Y a plus le bouquin : ~p~n",[get_livre(Bibli,"Hamlet")]),
ok.


Et l'ouput shell :


Recup du bouquin : {ok,{livre,"Hamlet","One upon a time ..."}}
Y a plus le bouquin : error
ok



RE: [JS] Boucle for..in Array et ajout de methodes via prototype = conflit?! - srm - 02-08-2013

Bah je te dis que tu utilises le pattern adapteur, si tu ne sais pas ce que c'est, je t'invite à te documenter
Et que tu l'utilises mal et que tu devrais utiliser une injection de dépendance pour avoir un code testable.
Et là encore, je t'invite à te documenter si tu ne vois pas de quoi je parle.

Et je dis que ce que tu as dit c'est du bullshit, car tu nous parles d'atomicité blabla, avec des tonnes d'explications pour nous sortir un banal adapteur. Qui n'est pas une mauvaise chose en soit, loin de là, mais que tout le monde utilise déjà (tout développeur avec un certain niveau de compétences) alors que toi tu es arrivé en nous disant "j'ai des idées et des concepts que souvent personnes ont et qui apportent des choses sympas", alors qu'en fait tu utilises juste un truc utilisé par tout le monde.


RE: [JS] Boucle for..in Array et ajout de methodes via prototype = conflit?! - niahoo - 02-08-2013

Mise à jour avec la traduction

http://paste.bouh.org/d64a5ecd8aa141558173b6f4575e5b6d

-module(biblio).
-export([livre/2,biblio/0]).
-export([add_livre/2]).
-export([new/0]).
-export([test/0]).


-record(livre, {titre,texte}).

livre(Titre,Texte) -> #livre{titre=Titre,texte=Texte}.

new() -> spawn(fun() -> biblio() end).

biblio() ->
biblio([]).

add_livre(Biblio, Book) ->
Biblio ! {add_book, Book, self()},
receive Response -> Response end.

get_livre(Biblio,Titre) ->
Biblio ! {get_livre, Titre, self()},
receive Response -> Response end.

replace_all_books(Biblio,Fn) when is_function(Fn) ->
Biblio ! {replace_all_books, Fn, self()},
receive Response -> Response end.


%% Internal ------------------------

biblio(Books) ->
NewBooks =
receive
{get_livre, BookName, From} ->
Result = orddict:find(BookName,Books),
From ! Result,
orddict:erase(BookName,Books)
; {add_book, #livre=NewBook, From} ->
NewDict = orddictConfusedtore(NewBook#livre.titre, NewBook, Books),
From ! ok,
NewDict
; {replace_all_books, Fn, From} ->
ReplaceBooks =
fun(_, Book, error) -> error
;(_, Book, NewBooks) ->
case Fn(Book)
of #livre{}=NewBook -> orddictConfusedtore(NewBook#livre.titre,NewBook,NewBooks)
; _ -> error
end
end,
{Reply, NewDict} =
case orddict:fold(ReplaceBooks,orddict:new(),Books)
of error -> {error,Books}
; SomeBooks -> {ok, SomeBooks}
end,
From ! Reply,
NewDict
; _ -> Books
end,
biblio(NewBooks).

test() ->
Bibli = new(),
add_livre(Bibli,livre("Hamlet", "Once upon a time ...")),
io:format("Recup du bouquin : ~p~n",[get_livre(Bibli,"Hamlet")]),
io:format("Y a plus le bouquin : ~p~n",[get_livre(Bibli,"Hamlet")]),
add_livre(Bibli,livre("Hamlet", "Once upon a time ...")),
add_livre(Bibli,livre("Othello", "Never Ends ...")),
add_livre(Bibli,livre("Batman", "Gotham City, 1953 ...")),
Traduc = replace_all_books(Bibli, fun traduire_livre/1),
io:format("Traduction terminee ? : ~p~n",[Traduc]),
io:format("Recup de Othello : ~p~n",[get_livre(Bibli,rot13("Othello"))]),
Traduc2 = replace_all_books(Bibli, fun (_Book) -> blabla end),
io:format("Traduction a l'arrache terminee ? : ~p~n",[Traduc2]),
io:format("Traduction dans l'autre sens ~p~n",[replace_all_books(Bibli, fun traduire_livre/1)]),
io:format("Recup de Hamlet : ~p~n",[get_livre(Bibli,"Hamlet")]),
io:format("Recup de Othello : ~p~n",[get_livre(Bibli,"Othello")]),
ok.


rot13(Str) ->
F = fun© when (C >= $A andalso C =< $M); (C >= $a andalso C =< $m) -> C + 13;
© when (C >= $N andalso C =< $Z); (C >= $n andalso C =< $z) -> C - 13;
© -> C
end,
lists:map(F, Str).

traduire_livre(#livre{titre=Tl,texte=Tx}=Book) ->
Book#livre{titre=rot13(Tl),texte=rot13(Tx)}.


Et l'ouput shell :


17> c(biblio).
biblio.erl:45: Warning: variable 'Book' is unused
{ok,biblio}
18> biblio:test().
Recup du bouquin : {ok,{livre,"Hamlet","Once upon a time ..."}}
Y a plus le bouquin : error
Traduction terminee ? : ok
Recup de Othello : {ok,{livre,"Bguryyb","Arire Raqf ..."}}
Traduction a l'arrache terminee ? : error
Traduction dans l'autre sens ok
Recup de Hamlet : {ok,{livre,"Hamlet","Once upon a time ..."}}
Recup de Othello : error
ok
19>

erf j'ai pas remplacé tous mes tuples par le record, c'est moche mais bon ça marche ...


RE: [JS] Boucle for..in Array et ajout de methodes via prototype = conflit?! - Xenos - 03-08-2013

Pour la traduction, j'opterai alors pour:
- Si on utilise le Conteneur, une méthode ".traduction" dans la classe Livre, méthode chargée de faire la traduction du Livre, et le code utilisateur doit donc itérer sur chaque élément du conteneur pour le traduire (donc, "on se retape la boucle for à chaque fois")
- Si on passe par la bibliothèque, celle-ci implémente une méthode "traduire()" qui se charge de traduire la bibliothèque entière (pas forcément Livre à Livre donc, je m'en fous de comment elle traduis)

Pour le Erlang, il va falloir que je prenne le temps de comprendre le fonctionnement... Pour le 1er code, si j'ai bien suivit, la bibliothèque est effectivement un "bête" conteneur, donc j'ai pas de problème avec ça, et ça me va.
En revanche, dans le 2nd code (si je le comprend bien, parce que je n'en suis vraiment pas sûr), Bibliothèque a une méthode qui itère sur chacun des objets qu'elle contient, et qui exécute la fonction de traduction pour chaque objet... Or, à mon sens, cela revient donc à traiter (depuis le code utilisateur qui appelle le "replace_all_book") le conteneur dans son ensemble, et là, je trouve cela mal venu, car une même classe peut être traitée "élément par élément", comme un Conteneur classique, mais aussi "dans son ensemble", comme un objet pur. Je trouve les deux peu compatibles, et je pense que ces deux notions devraient aller dans deux classes (ou équivalent) séparées, l'une étant le conteneur des données et l'autre étant l'objet représentant l'ensemble des données de façon indissociable.

En revanche, là, Oxman je ne te comprends pas pour plusieurs raisons:
- Adapter fait la transition entre deux classes, or un Conteneur n'est pas, à mon sens et comme exposé plus haut avec tout mon "bullshit sur les atomes", un objet/classe. Donc "Bibliothèque" n'est pas un Adapter, puisque ne "passe" pas une classe (ConteneurLivre) en une autre (Bibliothèque). Ou alors, tout est objet est un Adapter...
- Tu me dis que je fais le pattern Adapter mais qu'en fait je le fais pas comme il faut... Donc en fait, je ne fais pas ce pattern... Ou, dis autrement, t'es si sûr que ça, que je fais "juste" un pattern Adapter?!
- Tu me dis que je fais mal, mais pas comment faire bien... Cela fait un peu donneur de leçons je trouve.

Bon, et après, il faudra m'expliquer en quoi ce n'est pas testable comme code... ayant justement des classes qui masquent tout leur contenu et également leur fonctionnement au reste du code, je trouve au contraire cela très facile à tester.

(De manière annexe, pour le coté "y'a parfois des projets avec trop de classes qui ont 3 lignes", je rejoins en fait ce billet de blog. Un autre billet est également dans le même ordre d'idée, billet qui tend à prouver qu'à trop découper un problèmes en trop petits éléments, on perd plus de temps à recoller les éléments entre eux qu'à résoudre le problème général lui-même; et ce n'est pas un pancake du fond des landes qui le dit...).


RE: [JS] Boucle for..in Array et ajout de methodes via prototype = conflit?! - niahoo - 03-08-2013

Erlang

Dans les deux codes erlang, la bibliothèque est un processus, ce qui équivaut à un objet. Quand tu l'instancies via la fonction new/0 , on 'spawn' la fonction biblio/1 avec une liste vide (biblio([])).

Ton objet est représenté par la fonction biblio/1 qui se rappelle elle-même indéfiniment : tu constates que la fonction biblio/1 se termine par un appel à biblio(NewBooks).

Le second code présente la même fonction, sauf qu'elle peut recevoir un nouveau type de message (dans la définition classique de la POO, les objets communiquent entre-eux à l'aide de messages) de type {replace_all_books, function(), pid()}.

replace_all_books est le tag qui permet de différencier la méthode d'une autre, son nom en somme. pid() représente l'identifiant de processus de l'appelant de la méthode, vois-ça comme une référence vers l'objet appelant pour pouvoir lui répondre, ce dernier l'obtenant en appelant la fonction self/0.

Quand l'utilisateur appelle la fonction replace_all_books/2 avec en arguments la référence de la biblio et la fonction permettant de remplacer chaque bouquin, il ne sait pas comment va être appliqué cette fonction. Tout ce qu'il sait, c'est que pour chaque livre contenu dans la bibliothèque, il voudrait bien que cette fonction lui soit appliquée. Dans notre exemple, il envoie la fonction traduire_livre/1 qui reçoit un livre en argument.

Dans la "classe" biblio, celle-ci reçoit la fonction et la wrappe à l'intérieur de la fonction ReplaceBooks/3 qui va recevoir chaque livre un par un. Elle reçoit chaque livre dans son argument Book, puis elle lui applique la fonction transmise par l'utilisateur Fn en effectuant un case sur le résultat : si le résultat est de type #livre{} alors elle ajoute ce livre au nouveau set de bouquins (orddictConfusedtore/...), sinon elle retourne error. À l'itération suivante, soit on reçoit en argument NewBooks ce nouveau set de bouquins, soit on reçoit error. Si on reçoit error alors on retourne error indéfiniment.

Ansi, quand on applique cette fonction à l'ensemble de nos bouquins via un pli (orddict:fold/...), soit on reçoit effectivement un nouveau set de bouquins SomeBooks, tout s'est alors bien passé, et on définit NewBooks comme étant ce nouveau set de bouquins SomeBooks (via NewDict) pour la prochaine itération de boucle biblio/1, soit on garde Books, nos bouquins actuels comme livres et on envoie à l'utilisateur un message comme quoi il a chié dans la colle (Reply étant défini à error).

Au final, l'utilisateur de notre objet n'a a sa disposition que l'API définie au dessus du commentaire %% Internal ---. Comme le montre la fonction de test (située en deça de ce commentaire, malheur à moi). À aucun moment il n'a accès aux livres stockés directement, il ne traite pas l'objet biblio élément par élément. Il le considère toujours dans son ensemble comme un ensemble de livres. Il définit simplement une fonction qu'il aimerait voir appliquée à l'ensemble des livres contenus par la bibliothèque. C'est une simple nuance mais c'est toute la différence. La bibliothèque à tout loisir d'utiliser la fonction utilisateur diféremment, tant qu'elle l'applique bien aux livres. En tout cas, si la fonction ne se plie pas à l'exigence simple de récupérer un livre en sortie de fonction utilisateur, la bibliothèque refuse en bloc le changement. Il serait aisé de refuser uniquement les retours de fonctions qui ne donnent pas un livre tout en gardant ceux pour lesquels la fonction a ... fonctionné.

Citation :Bibliothèque a une méthode qui itère sur chacun des objets qu'elle contient, et qui exécute la fonction de traduction pour chaque objet... Or, à mon sens, cela revient donc à traiter (depuis le code utilisateur qui appelle le "replace_all_book") le conteneur dans son ensemble

Bien sûr que cela revient à traiter le conteneur dans son ensemble, n'est-ce pas ce que tu prônes depuis le début ? Tu voulais sûrement dire que cela revient à traiter le conteneur élément qu'il contient par élément qu'il contient, et, bien que ce soit le but au final, cette possibilité n'est jamais donnée à l'utilisateur. Il peut seulement proposer un nouveau livre en remplacement d'un autre via une fonction, et demander à la bibliothèque de bien vouloir le faire pour chacun de ses bouquins.


J'aime bien faire de la théorie OO avec un langage fonctionnel, c'est rafraîchissant Smile .

PHP

Ensuite, au sujet du pattern Adapter. Si j'ai bien suivi, oxman voudrait qu'on injecte à la bibliothèque son type de conteneur. (dans le premier code de Xenos, car dans le second, il n'y a que deux classes et on ne fait pas d'adapter avec deux classes). Mais comme on n'utilise pas un stockage en DB ou autre, je trouve que la Bibliothèque est libre de choisir le composant qu'elle veut pour stocker ses bouquins vu que ça ne "sort" pas, ça reste interne à cette classe.

Au sujet de la pureté : L'objet que je définis en erlang est pur, dans le sens ou il n'expose aucune variable publique, ou il ne communique avec l'extérieur qu'avec des messages, et ou il est impossible de l'extérieur d'influer sur l'exécution de son code privé ou quoi que ce soit. Il interargit avec des données de type livre et les stocke pleinement. Le second code PHP s'approche de cette pureté mais il stocke les livres en les découpant en titre et texte. Si l'objet livre change son implémentation "personnelle", par exemple en ajoutant une nouvelle donnée auteur, et ce sans changer son API jsque alors utilisée (membres titre et texte) alors la bibliothèque n'est plus en mesure de stocker proprement ces livres, ce qui est son seul et unique but, et devra avoir son code modifié.

putain j'a réussi à faire une tartine de 50 pages sur un code de 20 lignes ...


RE: [JS] Boucle for..in Array et ajout de methodes via prototype = conflit?! - srm - 03-08-2013

L'adapter c'est pas Bibliothèque c'est ConteneurLivre.
Le pattern en soit est juste, mais mal intégré pour pouvoir faire des tests unitaires.
Pour comprendre pourquoi ça n'est pas testable tu peux lire ça : http://julien-pauli.developpez.com/tutoriels/php/phpunit-avance/
Plus précisément la partie injection de dépendance.

De manière globale si tu suis tous les principes SOLID avancé dans ce tutoriel, ton code sera beaucoup plus propre et en plus testable.

Voilà en Scala ce que ça donne :


import scala.collection.mutable.ListBuffer


trait StorageLibrary
{

def store(item: Book): ListBuffer[Book]
def getByTitle(title: String): Option[Book]

}


class Book(protected var title: String, protected var text: String)
{

def getTitle = title
def getText = text

}


class Storage extends StorageLibrary
{

protected val books = new ListBuffer[Book]

def store(book: Book) = books += book
def getByTitle(title: String): Option[Book] = books.find(_.getTitle == title)

}


class Library(val storage: StorageLibrary)
{

def addBook(book: Book) = storage.store(book)
def getByTitle(title: String): Option[Book] = storage.getByTitle(title)

}



object Main extends App
{

val storage = new Storage
val library = new Library(storage)

val book = new Book("Lorem Ipsum", "Lorem ipsum dolor sit amet, consectetur adipisicing elit,...")

library.addBook(book)

val bookFound = library.getByTitle("Lorem Ipsum")

bookFound match {
case None => println("Book not found")
case Some(book) => println("Book found : " + book)
}
}



RE: [JS] Boucle for..in Array et ajout de methodes via prototype = conflit?! - Xenos - 03-08-2013

Short:
Code Erlang, ok
Code Scala: Library garantie que book === bookFound? C'est Storage qui assure que l'objet même est conservé (même emplacement mémoire), pas Library (et le comportement de Library ne doit pas être dicté par son initialisation via Storage). Ou alors Scala passe tout par référence et là, Ok.
Dans Storage (stocker/déstocker des références), le risque est l'injection: l'objet référencé peut être accéder de l'extérieur. Storage contient des données non fiables et ne devrait donc rien en faire. replace_all_books ne devrait pas être de son ressort: Storage ne peut pas considérer ses propres données comme fiables donc ne peut pas les traiter proprement.
2nd code PHP ne garantis pas que le Livre envoyé à Bibliothèque sera le même que celui récupéré (c'est le boulot de Conteneur ça). Donc si Livre change, Bibliothèque n'est pas "non-fiable" même avec titre/texte seul (sans l'auteur). On pourrait sinon utiliser "detDatas" ou "Serialize".

Idée clef:
Pour Storage, add() === get() mais données internes de Storage non fiables.
Pour un objet pur, add() !== get() mais données internes fiables, donc traitements possibles.


Long

@niahoo: ok! Merci de la tartine, le matin, c'est bienvenue Smile
Effectivement, là, je suis d'accord avec le code. Le livre ne reste pas dans la bibliothèque quand on le récupère, et on traite la bibliothèque dans son ensemble. En revanche, on ne peut donc pas "garantir" que get_livre("Hamlet") va renvoyer le même objet Livre que celui dans add_livre(Bibli, Livre("Hamlet")), puisque sinon, on présupposerai que la Bibliothèque stocke ses Livres de façon "unique" (on présuppose que la Bibliothèque stocke l'objet Livre et non pas une copie, ou ses données sérializées, ou autre).
Et de même, "pour chaque livre contenu dans la bibliothèque" la fonction replace_all_boks va être appliquée, d'accord, mais l'objet Livre auquel la fonction sera appliquée, rien ne me garanti que ce sera le même objet que je récupèrerai en sortie (la Bibliothèque peut donc très bien recevoir le message add_book(Livre), sérializer le Livre sans détruire l'objet d'origine, puis recevoir le message replace_all_books(callback) et pour chaque donnée sérializée, créer l'objet Livre associé, lui appliquer le callback, le resérializer puis le détruire).

Je suis donc pour cette implémentation (puisque get, add et forEach sont effectivement des messages) mais je suis alors contre l'idée que "Le Livre que je met via add sera le même que celui que je récupèrerai via get", ce qui serait faire un présupposé sur comment la Bibliothèque fonctionne.

Quand au 2nd code, effectivement, Bibliothèque fait le postulat que les données du Livre sont "titre" et "texte". Je devrais en effet changer ces deux méthodes "getTitle" et "getText" pour un "getDatas" ou pour un "Serialize" par exemple. Mais le fait de ne prendre que Titre et Texte n'est pas si buggé en soi, car la Bibliothèque, à mon sens, ne garantis pas le stockage des livres (c'est le Conteneur qui le garantis). L'astuce et la clef est justement là: la garantie fournie par Storage ne se retrouve pas dans Bibliothèque car je n'ai pas l'assurance du fonctionnement de Bibliothèque: je n'ai pas l'assurance que Bibliothèque utilisera Storage. Et à l'inverse, Storage stockant les références aux objets (ou leurs pointeurs), je n'ai alors pas la garantis dans Storage que les objets ne vont pas être changés par un autre bout de code (mais Storage garantis que le Livre sera conservé dans son intégrité).
Si je fais du C/C++, Storage va stocker des pointeurs, mais rien ne me garantis que ces pointeurs pointent encore vers un objet valide (je peux ajouter un objet à Storage qui stocke donc le pointeur, puis détruire l'objet, et Storage a alors un pointeur invalide). Or, si les données de Storage (internes) ne sont pas fiables car modifiables de l'extérieur, Storage ne devrait alors pas les traiter, en aucune façon.
Alors, oui, si je fais "replace_all_books", ce n'est pas Storage qui va traiter les Livres, mais callback. Sauf que du point de vue du code utilisateur, le message est envoyé à Storage, donc, c'est Storage qui fait le traitement pour l'utilisateur... C'est là où je trouve cela litigieux: si "bug" il y a, pour l'utilisateur, cela vient de Storage, alors qu'en fait, Storage ne garanti pas que les données stockées persisteront et seront fiables. Y'a un peu une forme "d'injection" dans Storage, puisqu'il stocke des références à des objets que je peux manipuler du dehors, et donc Storage ne devrait avoir le droit de ne rien faire si ce n'est stocker / déstocker ces références.


@Oxman: Merci aussi pour la tartine.
ok, je pensais que tu parlais du 2nd code.
Sur le code Scala, ce qui me gène alors, c'est que l'on a forcément (book === bookFound), et c'est là où cela me gène car alors je ne peux alors plus utiliser, comme StorageLibrary, une base de données qui sérialiserait l'objet puisque dans un tel cas, on aurait "book != bookFound" car 2 objets différents auront été créés (si on va ras les pâquerettes, on verrait que les pointeurs mémoire vers chaque objet ne sont pas les mêmes). C'est cette information, qui "filtre" hors de StorageLibrary, qui me gène.

C'est comme si, dans une architecture Client - Serveur - Base de données, le Client (Main) demandait son identifiant (Livre) au Serveur (Bibliothèque). Le serveur demande alors l'identifiant à la BDD (Storage). La BDD renvoie un identifiant. Le serveur retransmet ce même identifiant au Client. Le client reçoit alors non pas son identifiant sur le serveur, mais son identifiant sur la BDD. Normalement, le serveur devrait renvoyer une copie de l'identifiant, qui serait alors bel et bien l'identifiant du Client sur le Serveur (même si la valeur est égale, ce n'est pas le même objet, donc ce n'est pas garanti que la valeur sera égale). Si le client fait le test (Serveur.getId() == BDD.getId()), alors il récupère "true" (car les valeurs sont égales) mais s'il fait (Serveur.getId() === BDD.getId()) ou (&Serveur.getId() == &BDD.getId()) il récupère "false", car les objets sont différents.

Or, là, ce qui flotte dans l'air c'est que (book === bookFound) est une garantie fournie par Library (puisque j'attaque Library quand je fais addBook ou getByTitle). Or, cette garantie ne vient de la Library elle même, mais de Storage. Et je ne suis pas censé savoir comment Library a traité le Storage que je lui ai envoyé à sa construction.

Le addBook() == getBook() pour le Storage ne me pose encore pas trop de problème: je sais que Storage prend les objets eux-mêmes, et me les stocke. Mais en ce cas, Storage ne garanti rien sur les objets stockés, puisque le code utilisateur peut créer un objet, le mettre en Storage, puis modifier l'objet et donc l'objet que Storage possède (par "référence") sera aussi modifié. Si Storage ne peut rien garantir sur la fiabilité et l'immuabilité de ce qu'il contient, il ne peut faire aucun traitement lui-même.
Mais pour Library, addBook==getBook vient du Storage. Or, si je reprends les "Good coding Practices" (ok, je ne sais plus d'où elles viennent exactement, mais je les ai récupérées pendant mes cours), j'y lis "Objet usage must not rely on Objet's initialization". Or, le comportement de Library (addBook === getBook) provient de la manière de l'initialiser (avec Storage). Ca, ça me gène. Comme si j'insérai un objet dans mon SGDB et qu'en récupérant l'objet, je suis certain que c'est l'objet inséré et non une copie (alors qu'en fait, c'est bien une copie que je récupère).

Tout ceci n'étant valable que si Scala passe les arguments des appels de fonction par référence et non par valeur.


RE: [JS] Boucle for..in Array et ajout de methodes via prototype = conflit?! - srm - 03-08-2013

C'est le comportement normal, standard et attendu que book === bookFound.
Si tu sérialises oui tu vas casser book === bookFound, mais, si tu prévois de sérialiser alors, tu sais que tu ne pourras jamais avoir book === bookFound, dans ce cas tu redéfinis === pour que ça fasse une comparaison d'objet selon tes critères.


import scala.collection.mutable.ListBuffer


trait StorageLibrary
{

def store(book: Book): ListBuffer[Book]
def getByTitle(title: String): Option[Book]

}


class Book(protected var title: String, protected var text: String)
{

def getTitle = title
def getText = text
def equals(book: Book) = book.getTitle == getTitle

}


class Storage extends StorageLibrary
{

protected val books = new ListBuffer[Book]

def store(book: Book) = books += book
def getByTitle(title: String): Option[Book] = books.find(_.getTitle == title)

}


class Library(val storage: StorageLibrary)
{

def addBook(book: Book) = storage.store(book)
def getByTitle(title: String): Option[Book] = storage.getByTitle(title)

}



object Main extends App
{

val storage = new Storage
val library = new Library(storage)

val book = new Book("Lorem Ipsum", "Lorem ipsum dolor sit amet, consectetur adipisicing elit,...")
val sameBook = new Book("Lorem Ipsum", "Lorem ipsum dolor sit amet, consectetur adipisicing elit,...")

library.addBook(book)

val bookFound = library.getByTitle("Lorem Ipsum")

bookFound match {
case None => println("Book not found")
case Some(book) => {
println("Book found : " + book)
println("Book same : " + book.equals(sameBook))
}
}
}
Il n'y a pas d'autres façon de faire de toute façon Smile

En Scala on peut aussi utiliser une case class :


import scala.collection.mutable.ListBuffer


trait StorageLibrary
{

def store(book: Book): ListBuffer[Book]
def getByTitle(title: String): Option[Book]

}


case class Book(protected var title: String, protected var text: String)
{

def getTitle = title
def getText = text

}


class Storage extends StorageLibrary
{

protected val books = new ListBuffer[Book]

def store(book: Book) = books += book
def getByTitle(title: String): Option[Book] = books.find(_.getTitle == title)

}


class Library(val storage: StorageLibrary)
{

def addBook(book: Book) = storage.store(book)
def getByTitle(title: String): Option[Book] = storage.getByTitle(title)

}



object Main extends App
{

val storage = new Storage
val library = new Library(storage)

val book = Book("Lorem Ipsum", "Lorem ipsum dolor sit amet, consectetur adipisicing elit,...")
val sameBook = Book("Lorem Ipsum", "Lorem ipsum dolor sit amet, consectetur adipisicing elit,...")

library.addBook(book)

val bookFound = library.getByTitle("Lorem Ipsum")

bookFound match {
case None => println("Book not found")
case Some(book) => {
println("Book found : " + book)
println("Book same : " + (book == sameBook))
}
}
}

Je ne saurais te dire exactement le fonctionnement d'une case class, mais pour te montrer la différence niveau représentation :
Si j'affiche book d'une class normal ça me donne : Book@48ff2413
Si j'affiche book d'une case class ça me donne : Book(Lorem Ipsum,Lorem ipsum dolor sit amet, consectetur adipisicing elit,...)
Pour bien comprendre les case class : http://alblue.bandlem.com/2007/12/scala-introduction-to-scala-case.html

Je rajouterais que tu mélanges un peu tout je trouve, puisque en programmation on ne s'amuse jamais à comparer les objets ou leur id par référence dans un cas comme ça. Puisque ça ne sert à rien et n'apporte que des problèmes.

Tiens au passage, j'avais pas bien regardé le début du topic.
Ton benchmark est faux en plus, voici le bon :

<html>
<head>
<script>
function test()
{
var datas = [];
for (var i = 0;i<10000000;++i)
datas.push(0);

function forEachCallback()
{
var debut = (new Date()).getTime();
for (var i=0, s=datas.length; i<s; ++i)
{
var that = datas[i];
}
var fin = (new Date()).getTime();
console.log('forEachCallback: ', fin-debut);
}
forEachCallback();

var func = function (data)
{
}
var debut = (new Date()).getTime();
for (var i=0, s=datas.length; i<s; ++i) {
func(datas[i])
}
var fin = (new Date()).getTime();
console.log('for + callback: ', fin-debut);

var func = function ()
{
}
var debut = (new Date()).getTime();
datas.forEach(func);
var fin = (new Date()).getTime();
console.log('forEach: ', fin-debut);
}
</script>
</head>
<body onload="test()">
</body>
</html>

Et là... tada magie :
forEachCallback: 13
for + callback: 19
forEach: 439

Il y a bien un écart, mais déjà bien moins flagrant.


RE: [JS] Boucle for..in Array et ajout de methodes via prototype = conflit?! - Argorate - 03-08-2013

C'était quoi la question déjà?

Ah oui... comment contourner le problème de for..in JS avec le fait d'ajouter des méthode au prototype.

Hum, peut être qu'un modo pourrait scinder la conversation? Big Grin