Bonjour @avdyk, Merci pour ton retour, en ce qui concerne la clarté, la documentation Oracle dit (source) : its purpose is to help design more-comprehensible APIs so that by just reading the signature of a method, you can tell whether you can expect an optional value. This forces you to actively unwrap an Optional to deal with the absence of a value. Pour ce qui est de l'efficacité (performances ?), je n'ai pas testé, mais il semblerait que le fait de wrapper/unwrapper avec Optional n'impacte pas sur les performances. Optional est un objet comme un autre, mais si un Optional est null au lieu d'être vide, la feature perd son intérêt. P.-S. : Une petite syntaxe stream pour le coalesce en Java 8 : String a = Stream.of( null, null, "valeur defaut").filter( Objects::nonNull ).findFirst().get();
Bonjour, Utiliser isPresent() à la place de la comparaison du null n'apporte pas grand chose, ni en terme d'efficacité, ni en terme de clarté. Il est plus intéressant de profiter des méthodes d'Optional pour effectuer les opérations si l'Optional contient une valeur: Code : Sélectionner tout - Visualiser dans une fenêtre à part 123456final String test = " hello "; // essayer avec d'autres valeurs et null Optional.ofNullable(test) .map(String::trim) .filter(s -> s.length() > 0) .ifPresent(System.out::println); Si on veut fournir une valeur par défaut: Code : Sélectionner tout - Visualiser dans une fenêtre à part 123456String test = " hello "; // essayer avec d'autres valeurs et null final String withDefault = Optional.ofNullable(test) .map(String::trim) .filter(s -> s.length() > 0) .orElse("Valeur par défaut"); Tester si un Optional n'est pas null ne signifie pas qu'on a rien compris car un Optional pourrait être null. L'évolution des habitudes de programmation en Java veut qu'on ne teste plus si une collection, un tableau ou un Optional est null car c'est une mauvaise habitude de fournir une instance nulle. Il est recommandé de retourner Optional.empty(), une collection ou un tableau vide. Dans le cas d'un Optional, le null serait plutôt un bug et l'utilisateur de l'application ne pourrait rien y faire.
final String test = " hello "; // essayer avec d'autres valeurs et null Optional.ofNullable(test) .map(String::trim) .filter(s -> s.length() > 0) .ifPresent(System.out::println);
String test = " hello "; // essayer avec d'autres valeurs et null final String withDefault = Optional.ofNullable(test) .map(String::trim) .filter(s -> s.length() > 0) .orElse("Valeur par défaut");
Envoyé par eclesia Utiliser plutot Collections.EMPTY_LIST pour ne pas créer d'objet inutilement. Depuis Java5, il vaut même mieux privilégier Collections.emptyList() applicable aux listes avec génériques
Utiliser plutot Collections.EMPTY_LIST pour ne pas créer d'objet inutilement. Il y a aussi les @NotNull @Nonnull ...etc... dans divers projets de validation de contraintes.
Envoyé par bouye Je pense surtout que tu ne comprends que définir cela dans une interface permet de varier l’implémentation. Après il n'y a pas 36 millions de façon de faire pour invoquer une méthode d'une autre classe : soit elle est publique soit elle est package protected. Et comme justement on parle d'une interface ça élimine d'office le package protected. sisi, je comprends bien. Et, justement pour la raison que tu donnes toi meme, à mon avis, la methode de notification ne devrait pas etre dans l'interface. Pour s'en convaincre, il suffit d'imaginer ce que peuvent etre amenées à faire des fonctions/objets qui manipuleraient des Observables. De maniere assez evidente, ils vont vouloir s'enregistrer (donc la fonction addObserver est, évidemment, obligatoire). Par contre, ils ne voudront pas forcer une notification (appeler notifyObservers). Pire, comme cette fonction est dans l'interface, elle est public et peut donc etre appelée. C'est ca qui me derange. D'ailleurs, tu remarqueras que dans ton propre exemple, tu n'appelles pas la fonction de notification Encore une fois, à l'implementation, utiliser une fonction parait etre un choix evident. Par contre, à mon sens, il ne devrait pas etre dans l'interface. Sinon, par rapport aux moyens que tu donnes de corriger ce probleme, je suis d'accord avec toi mais je pense que c'est encore mieux de ne pas mettre cette contrainte quand on refait le pattern (si on utilise l'Observable du JDK, ce que tu proposes est necessaire mais tant qu'à refaire le pattern, autant ne pas remettre la fonction de notification).
Je pense surtout que tu ne comprends que définir cela dans une interface permet de varier l’implémentation. Après il n'y a pas 36 millions de façon de faire pour invoquer une méthode d'une autre classe : soit elle est publique soit elle est package protected. Et comme justement on parle d'une interface ça élimine d'office le package protected. Code Java : Sélectionner tout - Visualiser dans une fenêtre à part 123public interface ZeObserver { public void coucou(Result result); } Maintenant, ceci dit, on est pas obligé non-plus d’étendre l'interface dans l'objet observateur, il existe d'autres manières de faire qui permettent de cacher la chose et ainsi permettent d’éviter qu'une tierce-partie puisse invoquer cette méthode : Les classes internes : Code Java : Sélectionner tout - Visualiser dans une fenêtre à part 1234567machin.addObserver(new LocalObserver()); [...] private class LocalObserver implements ZeObserver { [...] } Les classes anonymes : Code Java : Sélectionner tout - Visualiser dans une fenêtre à part 123machin.addObserver(new ZeObserber() { [...] }); Et depuis le JDK8, les références de méthodes (et dans ce cas la méthode n'a pas besoin d’être publique, juste d’être conforme a la signature de la méthode de l'interface). Code Java : Sélectionner tout - Visualiser dans une fenêtre à part 12345machin.addObserver(this::zeObserverMethod); private void zeObserverMethod(Result result) { [...] } Et depuis le JDK8, aussi bien sur une lambda : Code Java : Sélectionner tout - Visualiser dans une fenêtre à part machin.addObserver(result -> { [...] });
public interface ZeObserver { public void coucou(Result result); }
machin.addObserver(new LocalObserver()); [...] private class LocalObserver implements ZeObserver { [...] }
machin.addObserver(new ZeObserber() { [...] });
machin.addObserver(this::zeObserverMethod); private void zeObserverMethod(Result result) { [...] }
machin.addObserver(result -> { [...] });
Envoyé par bouye Il serait difficile de faire autrement en Java justement car le contenu des interfaces est public Ce que je proposais, c'etait juste de ne pas mettre la fonction de notification dans l'interface. Apres tout, à l'implementation, je ne vois pas pourquoi je serais obligé d'utiliser une fonction (alors que je pourrais m'amuser à duppliquer une boucle foreach ). Plus sérieusement, c'est surtout exposer la fonction de notification qui me pique un peu les yeux ^^
Il serait difficile de faire autrement en Java justement car le contenu des interfaces est public. Il reste possible de le faire avec une classe abstraite mais alors on retombe sur le fait qu'il n'est pas possible d'avoir un héritage multiple. À noter que, oui, ces classes sont là depuis le JDK 1.0 mais qu'elles sont mises de coté dès le JDK 1.1 avec l'ajout des listeners et des events qui sont une autre manière d'aborder le pattern observer (pas réservée a AWT/Swing/GUI contrairement à ce que la plupart des gens peuvent croire).
J'avais bien remarqué que c'était une interface A mon avis, ce n'est pas une bonne chose de mettre dans l'interface cette methode pour les raisons que j'ai donné. De maniere générale, je pense qu'il est préférable de ne laisser public que ce qui doit l'etre (et donc, qui doit etre utilisé par une autre classe à un moment ou un autre - ce qui n'est pas le cas de la fonction qui notifie les observers). Mais bon, ce n'est que mon avis, chacun est libre de faire comme il veut ^^ Et sur le fond, ca ne change rien à la description du pattern.
Bonsoir hwoarang, et merci Mon Observable est une interface et une interface ne peut avoir que des méthodes de visibilité public, même si on ne précise pas le mot-clé public, on n'a pas de visibilité private-package, mais public pour une interface. D'ailleurs bien que le Observable du JDK soit une classe, sa méthode notifyObservers() est elle aussi public.
Bonjour, Le tuto me parait clair et efficace. Par contre, je trouve que declarer "public void notifyObservers(T data);" dans l'Observable est une erreur parce qu'elle rend publique une fonction interne de la classe (c'est elle et elle seule qui sait quand elle doit notifier les observers). Et, meme si je suis d'accord avec toi pour dire qu'une fonction pour notifier les observers est appropriée, à l'implémentation, je ne vois pas trop pourquoi on l'obligerait. Dans tous les cas, merci pour le tuto, ca aidera les debutants à comprendre ce pattern ^^
Bonjour @ddoumeche, Merci pour ton retour J'ai créé cet API car ce dernier répond à des problèmatiques que je n'ai pas trouvé sur le marché : Comment réaliser des aggrégats et des jointures sur des objets Java ? L'API Stream de Java 8 ne le permet pas de manière aisée.Mon client me fournit une extraction de sa BDD relationnel au format CSV/Excel (ou autre), comment dénormaliser la donnée assez rapidement pour placer son contenu dans une base NoSQL ?L'API peut être exploitée à d'autres fins aussi... N'hésitez pas à me faire des retours pour que je puisse améliorer l'API
Bonsoir Malick, Un grand merci pour ton aide. J'avais oublié d'ajouter cette partie, je viens de le faire à l'instant Cordialement,
Gugelhupf Super ton article. Toutefois il manque ce chapitre : TP CORBA Chapitre 5 : Implémentation (partie cliente)
Cela a l'air très jeune, mais extrêmement innovant et puissant. Pourquoi avoir eu besoin d'un tel projet dans une replication de base de données ?
J'ai mis 7 parce que je faisais référence à la version 7 du package summary. Je pense que je vais juste enlever le chiffre pour éviter les confusion. Celui qui clique sur JSR-311 verra que JAX-RS est apparue avec Java EE 6.
Note :JAX-RS est présent dans Java EE depuis la version 6.
Sous réserve de davantage de tests, il me semble que la méthode deepcopy récursive fonctionne bien. J'ai fait quelques tests avec prototype et avec class. Ci-dessous le code avec class : Code : Sélectionner tout - Visualiser dans une fenêtre à part 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687function clone( originalObject ){ if ( ( typeof originalObject !== 'object' ) || originalObject === null ){ throw new TypeError( "originalObject parameter must be an object which is not null" ); } function deepProto( originalObject ){ let deepCopy = Object.create( Object.getPrototypeOf( originalObject ) ); for ( let attribute in originalObject ){ deepCopy[ attribute ] = originalObject[ attribute ]; if ( typeof originalObject[ attribute ] === 'object' && originalObject[ attribute ] !== null ){ deepCopy[ attribute ] = deepProto( originalObject[ attribute ] ); } } return deepCopy; } return deepProto( originalObject ); } const kGetType = function( Obj ){ return Object.prototype.toString.call( Obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase(); }, kPays = Symbol( 'Pays' ), kPaysType = 'string', kSetPays = function( obj, value ){ if ( kGetType( value ) === kPaysType ){ obj[ kPays ] = value; } else { throw `Type des paramètres incompatibles. pays : ${ kPaysType } attendu`; } return obj; }; class Foo { constructor( levelName, obj, pays ){ this.levelName = levelName; this.deep = obj; this.Obj1 = { "test" : "hello", "Obj2" : { "test" : "bonjour" } } kSetPays( this, pays ); } method1(){ console.log("This method works !"); } get pays( ){ return this[ kPays ]; } set pays( value ){ kSetPays( this, value ); } }; let originalObject = new Foo( "Level 1", new Foo( "Level 2", new Foo( "Level 3", null, "USA" ), "France" ), "Belgique" ); let copy = clone( originalObject ); console.log( originalObject.Obj1 === copy.Obj1 ); console.log( originalObject.deep.Obj1 === copy.deep.Obj1 ); console.log( originalObject.deep.deep.Obj1 === copy.deep.deep.Obj1 ); console.log( originalObject.Obj1.Obj2 === copy.Obj1.Obj2 ); console.log( originalObject.deep.Obj1.Obj2 === copy.deep.Obj1.Obj2 ); console.log( originalObject.deep.deep.Obj1.Obj2 === copy.deep.deep.Obj1.Obj2 ); copy.deep.deep.pays = "Luxembourg"; console.log( originalObject.deep.deep.pays , copy.deep.deep.pays );
function clone( originalObject ){ if ( ( typeof originalObject !== 'object' ) || originalObject === null ){ throw new TypeError( "originalObject parameter must be an object which is not null" ); } function deepProto( originalObject ){ let deepCopy = Object.create( Object.getPrototypeOf( originalObject ) ); for ( let attribute in originalObject ){ deepCopy[ attribute ] = originalObject[ attribute ]; if ( typeof originalObject[ attribute ] === 'object' && originalObject[ attribute ] !== null ){ deepCopy[ attribute ] = deepProto( originalObject[ attribute ] ); } } return deepCopy; } return deepProto( originalObject ); } const kGetType = function( Obj ){ return Object.prototype.toString.call( Obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase(); }, kPays = Symbol( 'Pays' ), kPaysType = 'string', kSetPays = function( obj, value ){ if ( kGetType( value ) === kPaysType ){ obj[ kPays ] = value; } else { throw `Type des paramètres incompatibles. pays : ${ kPaysType } attendu`; } return obj; }; class Foo { constructor( levelName, obj, pays ){ this.levelName = levelName; this.deep = obj; this.Obj1 = { "test" : "hello", "Obj2" : { "test" : "bonjour" } } kSetPays( this, pays ); } method1(){ console.log("This method works !"); } get pays( ){ return this[ kPays ]; } set pays( value ){ kSetPays( this, value ); } }; let originalObject = new Foo( "Level 1", new Foo( "Level 2", new Foo( "Level 3", null, "USA" ), "France" ), "Belgique" ); let copy = clone( originalObject ); console.log( originalObject.Obj1 === copy.Obj1 ); console.log( originalObject.deep.Obj1 === copy.deep.Obj1 ); console.log( originalObject.deep.deep.Obj1 === copy.deep.deep.Obj1 ); console.log( originalObject.Obj1.Obj2 === copy.Obj1.Obj2 ); console.log( originalObject.deep.Obj1.Obj2 === copy.deep.Obj1.Obj2 ); console.log( originalObject.deep.deep.Obj1.Obj2 === copy.deep.deep.Obj1.Obj2 ); copy.deep.deep.pays = "Luxembourg"; console.log( originalObject.deep.deep.pays , copy.deep.deep.pays );
Envoyé par Gugelhupf Oui tu peux directement compiler en natif avec Java aussi, en utilisant des outils comme GCJ. il y a pu trop de nouveauté pour GCJ depuis un moment il y a excelsiorjet mais c'est très cher
Oui tu peux directement compiler en natif avec Java aussi, en utilisant des outils comme GCJ.