MEILLEURES PRATIQUES
DE DÉVELOPPEMENT
GRÂCE À GWT, GWTP ET JUKITO
CHRISTOPHER VIEL
CHRISTIAN GOUDREAU
Département de Génie...
Christopher
Viel
Lead Développeur
Arcbees
+ChristopherVielArcbees
Christian
Goudreau
BEO
Arcbees
+ChristianGoudreau
@imchr...
Partie 1
Présentation d’Arcbees
Partie 2
Résumé du MVP Model-View-Presenter
Partie 3
Survol de GWTP Framework MVP pour GWT...
Partie 5
Tests unitaires Exemples avec Mockito et Jukito
Partie 6
Meilleures pratiques
Partie 7
GWT 3.0
Slides disponibles...
Partie 1
Arcbees
Architectes de la Simplicité
Partie 1 Arcbees
Partie 1 Arcbees
Notre
mission
Créer des produits permettant à nos clients,
nos abeilles et la communauté
de développeurs d'optimiser leurs...
Origine
de Arcbees
Afin d'améliorer leur efficacité
de production, les deux cofondateurs ont
conçu GWT-Platform, un outil ...
Évolution
rapide
Rapidement, GWT-Platform a généré
une importante demande de soutien
de la part de ses utilisateurs
et les...
Reconnaissance
de Google
En moins d'un an, Google a remarqué
la jeune entreprise qui a été invitée à
donner une conférence...
Partie 1 Arcbees
Partie 2
MVP
Model View Presenter
➢ Model : Définit les données qui seront ultimement
gérées dans l’interface utilisateur.
➢ View : Affiche le modèle pour l...
Caractéristiques
du MVP
➢ Couplage faible entre
les différentes couches.
➢ Flot très simple,
presque linéaire.
➢ Communiqu...
Partie 2 MVP
M
V
P
Partie 2 MVP
MVC
comparé
à MVP
Quand l’utiliser
➢ Idéal lorsqu’un interface utilisateur est
nécessaire pour contrôler l’application.
➢ Pratique lorsqu’il...
Quand l’utiliser
➢ Simple à tester : Le faible
couplage permet de bien
isoler les couches.
➢ Réutilisation de code plus ai...
Quand l’utiliser
➢ Flexible : Il est facile de
remplacer n’importe quel
composant
tant qu’on respecte le contrat
défini pa...
➢ Le grand nombre d’interface à
gérer augmente la complexité du
code. Les IDEs modernes aident
néanmoins à atténuer ce
pro...
Partie 3
Survol de GWTP
Framework MVP pour GWT
➢ Repose sur GWT et GIN (GWT INjection).
➢ Abstraire la gestion du pattern MVP,
mais aussi imposer son utilisation.
➢ Abst...
➢ Permet un découplage plus poussé et
rend le code plus modulaire (DIP, IoC).
○ Aide à atteindre de meilleures
pratiques,
...
➢ Limiter le code redondant
à écrire qui est habituellement
requis en GWT.
➢ Séparer les phases d’initialisation
en phases...
Exemple: Initialisation et Bootstrapper
Configuration via le module GWT
public class ClientModule extends AbstractGinModul...
➢ Permet de réagir à différents
stimuli provenant d’ailleurs
dans l’application.
➢ Permet d’éviter de coupler des
classes ...
➢ Isoler le comportement
du presenter pour
chaque phase de sa vie.
➢ Comportements isolés
donc plus simple à
tester unitai...
Exemple: Aperçu des méthodes du lifecycle
public class EditManufacturerPresenter
extends PresenterWidget<MyView> {
interfa...
➢ Gère la navigation entre
les places. Beaucoup de code
à écrire autrement.
➢ Offre plusieurs façons de
présenter le token...
Exemple: Utiliser le Place Manager
Révéler un name token
private final PlaceManager placeManager;
@Inject
EditManufacturer...
➢ Permet de configurer et restreindre l’
accès à certains tokens.
➢ Possibilité de configurer un gatekeeper
par défaut.
➢ ...
Exemple: Utiliser un Gatekeeper
Gatekeeper
public abstract class AclGatekeeper implements GatekeeperWithParams {
private f...
➢ Le coeur des fonctions de GWTP :
○ Accès au lifecycle,
○ Gestion centralisée des événements
○ Possède une vue,
○ Communi...
Exemple: Un PresenterWidget
Le PresenterWidget
public class DashboardPresenter extends PresenterWidget<MyView> {
interface...
➢ Un PresenterWidget spécialisé.
➢ Doit être associé à un proxy.
➢ Initialisation uniquement lorsque
nécessaire: améliore ...
Exemple: Un Presenter
Le PresenterWidget
public class DashboardPresenter extends Presenter<MyView, MyProxy> {
interface My...
➢ Un PresenterWidget ayant un popup
comme vue.
➢ Les mêmes avantages qu’un
PresenterWidget: slots, lifecycle.
➢ Délègue l’...
Comunication Serveur
Rest-Dispatch
Partie 4
Partie 4 Rest-Dispatch
Amené à disparaître dans GWT.
Nécessite beaucoup de code pour
un cas simple. Renforcit le
couplage ...
➢ Implémentation partielle
de JSR-311 1.1 (JAX-RS).
➢ Définition d’interface et
configuration de l’API
au moyen d’annotati...
➢ Usage plus explicite que la syntaxe
de base.
➢ Les interfaces et annotations peuvent
être réutilisé sur l’implémentation...
Exemple: Configurer et utiliser Rest-Dispatch
Le module GWT Les paths
Le module GIN L’interface de ressource
<?xml version...
Exemple: Configurer et utiliser Rest-Dispatch (suite)
Utilisation de la ressource
static final SingleSlot<PresenterWidget<...
Tests unitaires
Tester GWTP et Rest-Dispatch
avec Mockito et Jukito
Partie 5
Partie 5 Tests unitaires
Jukito
Jukito permet à vos tests d’utiliser l’
injection de dépendances. Peu
importe le type de t...
➢ Mocking automatique
➢ Injection des mocks
➢ Annotation @ALL
➢ Léger
➢ S'adapte à toutes tailles de test
➢ Facile d’insta...
Exemples
Sur GitHub
Mockito et Jukito:
ManufacturerDetailPresenter: https://goo.
gl/27NsKL
ManufacturerDetailPresenterTest...
Mockito : Initialisation
CAS DE TEST TEST
@Inject
ManufacturerDetailPresenter(
EventBus eventBus,
MyView view,
MyProxy pro...
Mockito : Test
CAS DE TEST TEST
@Override
protected void onReveal() {
List<ActionType> actions;
if (createNew) {
actions =...
Jukito : Initialisation
CAS DE TEST TEST
@RunWith(JukitoRunner.class)
public class ManufacturerDetailPresenterTest {
// SU...
Jukito : Test
CAS DE TEST TEST
@Override
protected void onReveal() {
List<ActionType> actions;
if (createNew) {
actions = ...
Jukito : Utiliser @All pour tester plusieurs cas
CAS DE TEST TEST
@Inject
PersonRenderer(
Messages messages,
@Assisted Mod...
Jukito : Utiliser @All pour tester plusieurs cas (suite)
TEST TEST
@RunWith(JukitoRunner.class)
public class PersonRendere...
Jukito : Utilitaires pour tester Rest-Dispatch
CAS DE TEST TEST
public void onSave(ManufacturerDto manufacturer) {
manufac...
GWT
Meilleures Pratiques
Partie 6
Avoid widgets
as much as you can
BEST PRACTICE 1
Partie 6
WIDGETS
ARE HEAVY.
BECAUSE
Partie 6
TAKE CONTROL
OF YOUR HTML!
Full Event
Mechanism
BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN
Partie 6
Even if you don’t need it
Full Event
Mechanism
BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN
Partie 6
How to attach
event handler
to elements ?
BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN
Partie 6
Use
widget
To encapsulate complex
component to reuse
» prefer PresenterWidget if the component
has a lot of business logic...
Cell widgets (CellTable, CellList…)
HtmlPanel
Exceptions
BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN
Partie 6
Use an
Event Bus
BEST PRACTICE 2
Partie 6
Use Dependency
Injection
BEST PRACTICE 3
Partie 6
Use
MVP Pattern
BEST PRACTICE 4
Partie 6
Make
Navigation Easy
BEST PRACTICE 5
Partie 6
Use directly
your name tokens
BP 5 : MAKE NAVIGATION EASY
Partie 6
Use CSS
as much as you can
BEST PRACTICE 6
Partie 6
Loas everything
you can in the html page
BEST PRACTICE 7
Partie 6
UNIT
TESTS
Partie 6
Test each
protected and public
methods of your presenters.
UNIT TESTS
Partie 6
CODE
REVIEW
Partie 6
Improve quality
of your code
CODE REVIEW
Partie 6
Detect Bugs
earlier
CODE REVIEW
Partie 6
A way to learn
from your peers
CODE REVIEW
Partie 6
Web based
code review system
CODE REVIEW
Partie 6
CODE REVIEW
Best practices
Partie 6
TAKE CONTROL
OF YOUR HTML!USE A STYLE GUIDE !
Address/fix
all comments
before you merge.
CODE REVIEW Best Practice
Partie 6
CONTINUOUS
INTEGRATION
Partie 6
Partie 6
The first to do code review
is your CI Server
CONTINUOUS INTEGRATION
Partie 6
Partie 6
Enforce
Check Style
CONTINUOUS INTEGRATION
Partie 6
ACTORS
Partie 6
Partie 6
Partie 6
Designer
Partie 6
Web Integrators
Partie 6
Developpers
Partie 6
Partie 6
Partie 6
Partie 6
Partie 6
SUCCESS
Partie 6
GWT 3.0
Partie 7
Java 8 Support
Partie 7
JSInterop
Partie 7
Export
any Java code
to Javascript
JSinterop
Partie 7
Export any
Javascript API
to Java
Partie 7
JSinterop
Subclassing
JavaScript
Objects
Partie 7
JSinterop
Web component
Material design
in GWT
Partie 7
JSinterop
Start to
experiment
with GWT 2.7
» Turn it on with a flag
-XjsInteropMode
» Subject to change !
Partie 7
JSinterop
MERCI
QUESTIONS ?
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Uni.sherbrooke 2015   créez la meilleur application grâce à gwt, gwtp et jukito
Prochain SlideShare
Chargement dans…5
×

Uni.sherbrooke 2015 créez la meilleur application grâce à gwt, gwtp et jukito

739 vues

Publié le

MEILLEURES PRATIQUES
DE DÉVELOPPEMENT
GR CE À GWT, GWTP ET JUKITO
Conference by Christian Goudreau et Christopher Viel
au Département Génie Informatique de l'université de Sherbrooke.
Christopher Viel is Software Engineer at Arcbees.
You can follow Christian on Google+ :+ChristopherVielArcbees

Christian Goudreau is BEE-EO AND CO-FOUNDER
at Arcbees.
You can follow Christian on Twitter : @imchrisgoudreau


Christian Goudreau, ArcBees’ CEO, is a self-made entrepreneur with significant experience in project management. Christian has been managing major software development projects since his early teens, and therefore has quickly learned how to juggle heavy responsibilities and deliver.

A talented guest speaker, recognized expert in software architecture and developer tools, his services are much sought-after, not only in Quebec but also in Europe and the United States, where he takes great pleasure in sharing his technical knowledge and his passion for business.

Christian Goudreau was named Young Business Person of the Year, technology & research division, at the Jeune personnalité d’affaires Banque Nationale competition organized by the Jeune chambre de commerce de Québec (JCCQ), in 2012. He was also awarded the Creativity and Innovation Prize, and the Grand Prize at the 2013 Annual LOJIQ awards (the Quebec International Youth Offices).

Publié dans : Technologie
0 commentaire
1 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
739
Sur SlideShare
0
Issues des intégrations
0
Intégrations
8
Actions
Partages
0
Téléchargements
11
Commentaires
0
J’aime
1
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

Uni.sherbrooke 2015 créez la meilleur application grâce à gwt, gwtp et jukito

  1. 1. MEILLEURES PRATIQUES DE DÉVELOPPEMENT GRÂCE À GWT, GWTP ET JUKITO CHRISTOPHER VIEL CHRISTIAN GOUDREAU Département de Génie Informatique Université de Sherbrooke Juin 2015
  2. 2. Christopher Viel Lead Développeur Arcbees +ChristopherVielArcbees Christian Goudreau BEO Arcbees +ChristianGoudreau @imchrisgoudreau
  3. 3. Partie 1 Présentation d’Arcbees Partie 2 Résumé du MVP Model-View-Presenter Partie 3 Survol de GWTP Framework MVP pour GWT Partie 4 Communication serveur Rest-Dispatch Slides disponibles à https://goo.gl/PBzmwL
  4. 4. Partie 5 Tests unitaires Exemples avec Mockito et Jukito Partie 6 Meilleures pratiques Partie 7 GWT 3.0 Slides disponibles à https://goo.gl/PBzmwL
  5. 5. Partie 1 Arcbees Architectes de la Simplicité
  6. 6. Partie 1 Arcbees
  7. 7. Partie 1 Arcbees
  8. 8. Notre mission Créer des produits permettant à nos clients, nos abeilles et la communauté de développeurs d'optimiser leurs processus, habitudes et façons de penser, en créant continuellement de la valeur et en transformant la façon dont ils travaillent. Partie 1 Arcbees
  9. 9. Origine de Arcbees Afin d'améliorer leur efficacité de production, les deux cofondateurs ont conçu GWT-Platform, un outil open source de création d'applications web doté d'une incroyable capacité d'adaptation accélérant la vitesse de développement des applications. Partie 1 Arcbees
  10. 10. Évolution rapide Rapidement, GWT-Platform a généré une importante demande de soutien de la part de ses utilisateurs et les deux entrepreneurs n'ont eu d'autres choix que de créer officiellement Arcbees le 16 juillet 2010 afin de répondre à ces requêtes. Partie 1 Arcbees
  11. 11. Reconnaissance de Google En moins d'un an, Google a remarqué la jeune entreprise qui a été invitée à donner une conférence au Google I/O, puis, en 2012, Christian a été nommé sur le Comité de direction de GWT, groupe en charge de la direction et de l'évolution du framework. Partie 1 Arcbees
  12. 12. Partie 1 Arcbees
  13. 13. Partie 2 MVP Model View Presenter
  14. 14. ➢ Model : Définit les données qui seront ultimement gérées dans l’interface utilisateur. ➢ View : Affiche le modèle pour l’utilisateur. Généralement passive: seulement de la logique d’ affichage. Délègue les interactions utilisateur (events) au presenter. ➢ Presenter : Récupère et met à jour le modèle. Interprète le modèle les données pour les transférer à la vue. Partie 2 MVP M V P
  15. 15. Caractéristiques du MVP ➢ Couplage faible entre les différentes couches. ➢ Flot très simple, presque linéaire. ➢ Communique exclusivement au moyen d’interfaces. ➢ Vue passive. Ne prend aucune décision et notifie le presenter. Partie 2 MVP
  16. 16. Partie 2 MVP M V P
  17. 17. Partie 2 MVP MVC comparé à MVP
  18. 18. Quand l’utiliser ➢ Idéal lorsqu’un interface utilisateur est nécessaire pour contrôler l’application. ➢ Pratique lorsqu’il y a beaucoup d’édition de données (formulaires, CRUD, etc.) ➢ Simple à mettre en place lorsqu’une communication entre client - serveur est requise. Partie 2 MVP Quand l’utiliser
  19. 19. Quand l’utiliser ➢ Simple à tester : Le faible couplage permet de bien isoler les couches. ➢ Réutilisation de code plus aisée : Le flot plus simple et l’utilisation intensive d’interfaces permet de mieux extraire le code réutilisable. Partie 2 MVP Les bénéfices et les désavantages
  20. 20. Quand l’utiliser ➢ Flexible : Il est facile de remplacer n’importe quel composant tant qu’on respecte le contrat défini par les interfaces. ➢ Peut se combiner à d’autres design patterns. Partie 2 MVP Les bénéfices et les désavantages
  21. 21. ➢ Le grand nombre d’interface à gérer augmente la complexité du code. Les IDEs modernes aident néanmoins à atténuer ce problème (détection des implémentations). ➢ La vue passive introduit beaucoup de code boilerplate: beaucoup d’aller-retour entre le presenter et la vue. Partie 2 MVP Les bénéfices et les désavantages
  22. 22. Partie 3 Survol de GWTP Framework MVP pour GWT
  23. 23. ➢ Repose sur GWT et GIN (GWT INjection). ➢ Abstraire la gestion du pattern MVP, mais aussi imposer son utilisation. ➢ Abstraire la gestion cliente: initialiser l’ application, navigation, communication avec le serveur. ➢ Réduire le code à écrire habituellement requis en GWT. ➢ Optimisations: Proxy, code splitting... GWTP Partie 3 GWTP
  24. 24. ➢ Permet un découplage plus poussé et rend le code plus modulaire (DIP, IoC). ○ Aide à atteindre de meilleures pratiques, ○ Classes immutables, ○ Créer de nouvelles dépendances devient plus simple : Extraction de code vers des collaborateurs (SRP). ➢ Tend à rendre les tests plus simples: Jukito, GuiceBerry… ➢ En bref : Aide à atteindre les principes SOLID Injection de dépendances Partie 3 GWTP
  25. 25. ➢ Limiter le code redondant à écrire qui est habituellement requis en GWT. ➢ Séparer les phases d’initialisation en phases logiques simple à étendre ou remplacer. ➢ Permet d’utiliser l’injection de dépendances plus tôt dans le programme. ➢ Configuration initiale à un seul endroit. Références : DefaultModule, PreBootstrapper, Bootstrapper Initialisation de l’application Partie 3 GWTP
  26. 26. Exemple: Initialisation et Bootstrapper Configuration via le module GWT public class ClientModule extends AbstractGinModule { @Override protected void configure() { install(new DefaultModule.Builder() .defaultPlace(NameTokens.LOGIN) .errorPlace(NameTokens.LOGIN) .unauthorizedPlace(NameTokens.UNAUTHORIZED) .build()); install(new ApplicationModule()); bind(ResourceLoader.class).asEagerSingleton(); } } <?xml version="1.0" encoding="UTF-8"?> <module> <inherits name="com.gwtplatform.mvp.MvpWithEntryPoint"/> <source path="client"/> <source path="shared"/> <set-configuration-property name="gwtp.bootstrapper" value="com.arcbees.demo.client.DefaultBootstrapper"/> <extend-configuration-property name="gin.ginjector.modules" value="com.arcbees.demo.client.ClientModule"/> </module> Bootstrapper (facultatif!) Configuration via GIN public class DefaultBootstrapper implements Bootstrapper { private final PlaceManager placeManager; @Inject DefaultBootstrapper(PlaceManager placeManager) { this.placeManager = placeManager; } @Override public void onBootstrap() { // Initialize states then reveal the app: placeManager.getCurrentPlaceRequest(); } }
  27. 27. ➢ Permet de réagir à différents stimuli provenant d’ailleurs dans l’application. ➢ Permet d’éviter de coupler des classes qui n’ont aucun point commun. ➢ Implémentation générique et de l’Observer. Références : addRegisteredHandler, addVisibleHandler, @ProxyEvent Plus de détails: https://blog.arcbees.com/?p=699 Évenements (GWT, EventBus) Partie 3 GWTP
  28. 28. ➢ Isoler le comportement du presenter pour chaque phase de sa vie. ➢ Comportements isolés donc plus simple à tester unitairement. Lifecycle (GWTP) Références : onBind, onReveal, onReset, onHide, onUnbind, prepareFromRequest Partie 3 GWTP
  29. 29. Exemple: Aperçu des méthodes du lifecycle public class EditManufacturerPresenter extends PresenterWidget<MyView> { interface MyView extends View { } @Inject EditManufacturerPresenter( EventBus eventBus, MyView view) { super(eventBus, view); } @Override protected void onBind() {} @Override protected void onReveal() {} @Override protected void onReset() {} @Override protected void onHide() {} @Override protected void onUnbind() {} }
  30. 30. ➢ Gère la navigation entre les places. Beaucoup de code à écrire autrement. ➢ Offre plusieurs façons de présenter le token : le hash dans l’URI, après le #. ➢ Possibilité de rediriger vers une même page lorsqu’un token inconnu est accédé. Page 404 à- la GWTP. Références : PlaceManager, PlaceRequest, TokenFormatter Navigation (GWTP) Partie 3 GWTP
  31. 31. Exemple: Utiliser le Place Manager Révéler un name token private final PlaceManager placeManager; @Inject EditManufacturerPresenter( EventBus eventBus, MyView view, PlaceManager placeManager) { super(eventBus, view); this.placeManager = placeManager; } @Override public void cancel() { PlaceRequest placeRequest = new PlaceRequest.Builder() .nameToken(NameTokens.MANUFACTURERS) .build(); placeManager.revealPlace(placeRequest); } Récupérer le name token courant private final PlaceManager placeManager; @Inject HeaderPresenter( EventBus eventBus, MyView view, PlaceManager placeManager) { super(eventBus, view); this.placeManager = placeManager; } @Override protected void onReset() { PlaceRequest currentPlace = placeManager.getCurrentPlaceRequest(); String currentNameToken = currentPlace.getNameToken(); getView().setActiveMenuItem(currentNameToken); }
  32. 32. ➢ Permet de configurer et restreindre l’ accès à certains tokens. ➢ Possibilité de configurer un gatekeeper par défaut. ➢ Permet de rediriger vers une même page lorsque qu’un accès non autorisé est détecté. Page 401 à-la GWTP. ➢ Attention: Si la page protégée par un gatekeeper accède à des ressources serveur, le serveur doit lui aussi protéger ses ressources! Références : Gatekeeper, GatekeeperWithParams, @UseGatekeeper, @GatekeeperParams, @NoGatekeeper, @DefaultGatekeeper Sécurité (GWTP) Partie 3 GWTP
  33. 33. Exemple: Utiliser un Gatekeeper Gatekeeper public abstract class AclGatekeeper implements GatekeeperWithParams { private final CurrentSession currentSession; private Set<String> permissions; @Inject AclGatekeeper(CurrentSession currentSession) { this.currentSession = currentSession; this.permissions = new HashSet<>(); } @Override public GatekeeperWithParams withParams(String[] params) { permissions = new HashSet<>(Arrays.asList(params)); return this; } @Override public boolean canReveal() { return currentSession.isLoggedIn() && currentSession.hasPermissions(permissions); } } Utilisation du Gatekeeper public class EditGroupMembersPresenter extends Presenter<MyView, MyProxy> { interface MyView extends View { } @ProxyStandard @NameToken(NameTokens.EDIT_GROUP_MEMBERS) @UseGatekeeper(AclGatekeeper.class) @GatekeeperParams({"GROUPS_UPDATE", "MEMBERS_UPDATE"}) interface MyProxy extends ProxyPlace<EditGroupMembersPresenter> { } /* snip */ }
  34. 34. ➢ Le coeur des fonctions de GWTP : ○ Accès au lifecycle, ○ Gestion centralisée des événements ○ Possède une vue, ○ Communication vue vers presenter via un UiHandlers. ➢ Permet de développer des composants réutilisables et testables. ➢ Permet de définir des Slots pour imbriquer d’autres PresenterWidgets. Références : PresenterWidget, UiHandlers, View, OrderedSlot, SingleSlot, Slot Presenter Widgets Partie 3 GWTP
  35. 35. Exemple: Un PresenterWidget Le PresenterWidget public class DashboardPresenter extends PresenterWidget<MyView> { interface MyView extends View { } static final Slot<PresenterWidget<?>> SLOT_TEMPLATE = new SingleSlot<>(); private final Presenter<?, ?> presenter; @Inject DashboardPresenter( EventBus eventBus, MyView view, Presenter<?, ?> presenter) { super(eventBus, view); this.presenter = presenter; } @Override protected void onBind() { setInSlot(SLOT_TEMPLATE, presenter); } } Sa vue public class DashboardView extends ViewImpl implements MyView { interface Binder extends UiBinder<Widget, DashboardView> { } @UiField SimplePanel template; @Inject DashboardView(Binder uiBinder) { initWidget(uiBinder.createAndBindUi(this)); bindSlot(DashboardPresenter.SLOT_TEMPLATE, template); } } Sa configuration GIN public class DashboardModule extends AbstractPresenterModule { @Override protected void configure() { bindPresenterWidget(DashboardPresenter.class, DashboardPresenter.MyView.class, DashboardView.class); } }
  36. 36. ➢ Un PresenterWidget spécialisé. ➢ Doit être associé à un proxy. ➢ Initialisation uniquement lorsque nécessaire: améliore le chargement initial. ➢ Peut être associé à un name token (place). ➢ Permet de définir des NestedSlots pour imbriquer d’autres Presenters sans couplage. Références : Presenter, NestedSlot, Proxy, ProxyPlace, @ProxyStandard, @NameToken Presenter Partie 3 GWTP
  37. 37. Exemple: Un Presenter Le PresenterWidget public class DashboardPresenter extends Presenter<MyView, MyProxy> { interface MyView extends View { } @ProxyStandard @NameToken(NameTokens.DASHBOARD) @UseGatekeeper(LoggedInGatekeeper.class) interface MyProxy extends ProxyPlace<DashboardPresenter> { } static final NestedSlot SLOT_TEMPLATE = new NestedSlot(); @Inject DashboardPresenter( EventBus eventBus, MyView view, MyProxy proxy) { super(eventBus, view, proxy, ApplicationPresenter.SLOT_MAIN); } } public class DashboardView extends ViewImpl implements MyView { interface Binder extends UiBinder<Widget, DashboardView> { } @UiField SimplePanel template; @Inject DashboardView(Binder uiBinder) { initWidget(uiBinder.createAndBindUi(this)); bindSlot(DashboardPresenter.SLOT_TEMPLATE, template); } } Sa configuration GIN public class DashboardModule extends AbstractPresenterModule { @Override protected void configure() { bindPresenter(DashboardPresenter.class, DashboardPresenter.MyView.class, DashboardView.class, DashboardPresenter.MyProxy.class); } } Sa vue
  38. 38. ➢ Un PresenterWidget ayant un popup comme vue. ➢ Les mêmes avantages qu’un PresenterWidget: slots, lifecycle. ➢ Délègue l’affichage / masquage du popup au presenter. ➢ Abstrait le concept de positionnement du popup. Références : PopupView, PopupViewImpl, addToPopupSlot, PopupPositioner Popups Partie 3 GWTP
  39. 39. Comunication Serveur Rest-Dispatch Partie 4
  40. 40. Partie 4 Rest-Dispatch Amené à disparaître dans GWT. Nécessite beaucoup de code pour un cas simple. Renforcit le couplage entre client et serveur. Plus simple de conserver le code client et serveur compatible. RPC REST Permet de communiquer avec n’ importe quel API REST. Presque tous les serveurs permettent de faire du REST : à la mode. Plus facile de modifier l’ API et oublier de mettre le code client à jour.
  41. 41. ➢ Implémentation partielle de JSR-311 1.1 (JAX-RS). ➢ Définition d’interface et configuration de l’API au moyen d’annotations. ➢ Code client (GWT) seulement. L’API doit être codé en utilisant une autre technologie. ➢ Possibilité de créer ses propres extensions. Rest-Dispatch Partie 4 Rest-Dispatch
  42. 42. ➢ Usage plus explicite que la syntaxe de base. ➢ Les interfaces et annotations peuvent être réutilisé sur l’implémentation serveur (DRY). ➢ Plus simple de conserver un API privé compatible à la fois sur le client et le serveur. ➢ Désavantage: Perte du type safety lors de la création de callbacks. Extension : Delegates Partie 4 Rest-Dispatch
  43. 43. Exemple: Configurer et utiliser Rest-Dispatch Le module GWT Les paths Le module GIN L’interface de ressource <?xml version='1.0' encoding='UTF-8'?> <module> <inherits name="com.gwtplatform.dispatch.rest.delegates.ResourceDelegate"/> <inherits name='com.gwtplatform.mvp.MvpWithEntryPoint'/> <source path="client"/> </module> public class ResourcesModule extends AbstractGinModule { @Override protected void configure() { install(new RestDispatchAsyncModule.Builder() /* Additional configurations */ .build()); } @Provides @RestApplicationPath String applicationPath() { String baseUrl = GWT.getHostPageBaseURL(); if (baseUrl.endsWith("/")) baseUrl = baseUrl.substring(0, baseUrl.length() - 1); return baseUrl + API; } } public static class DashboardPaths { public static final String DASHBOARD_ID = "dashboard-id"; public static final String DASHBOARDS = "/dashboards"; public static final String DASHBOARD = "/{" + DASHBOARD_ID + "}"; } @Path(DASHBOARDS) public interface DashboardResource { @GET List<Dashboard> getAll(); @GET @Path(DASHBOARD) Dashboard get(@PathParam(DASHBOARD_ID) int id); @PUT @Path(DASHBOARD) void update(@PathParam(DASHBOARD_ID) int id, Dashboard dashboard); }
  44. 44. Exemple: Configurer et utiliser Rest-Dispatch (suite) Utilisation de la ressource static final SingleSlot<PresenterWidget<?>> SLOT_TEMPLATE = new SingleSlot<>(); private final ResourceDelegate<DashboardResource> dashboards; private final DashboardTemplateFactory templateSelector; @Inject DashboardPresenter( EventBus eventBus, MyView view, MyProxy proxy, ResourceDelegate<DashboardResource> dashboards, DashboardTemplateFactory templateFactory) { super(eventBus, view, proxy, ApplicationPresenter.SLOT_MAIN); this.dashboards = dashboardService; this.templateFactory = templateFactory; } @Override protected void onReveal() { dashboards.withCallback(new AbstractAsyncCallback<Dashboard>() { @Override public void onSuccess(Dashboard result) { PresenterWidget<?> template = templateFactory.createTemplate(result); setInSlot(SLOT_TEMPLATE, template); } }).get(DashboardSettings.DEFAULT_DASHBOARD_ID); }
  45. 45. Tests unitaires Tester GWTP et Rest-Dispatch avec Mockito et Jukito Partie 5
  46. 46. Partie 5 Tests unitaires Jukito Jukito permet à vos tests d’utiliser l’ injection de dépendances. Peu importe le type de test — unitaire, intégration ou quoi que ce soit de loufoque —, le code redondant dû au mocking diminuera grandement. Rapidement, vous ne pourrez plus vous passez de sa syntaxe sécuritaire et basée sur les annotations!
  47. 47. ➢ Mocking automatique ➢ Injection des mocks ➢ Annotation @ALL ➢ Léger ➢ S'adapte à toutes tailles de test ➢ Facile d’installation Fonctionalités Partie 5 Tests unitaires
  48. 48. Exemples Sur GitHub Mockito et Jukito: ManufacturerDetailPresenter: https://goo. gl/27NsKL ManufacturerDetailPresenterTest: https://goo. gl/irq5rV ManufacturerDetailPresenterMockitoTest: https: //goo.gl/EIlj47 Jukito @All: PersonRenderer: https://goo.gl/o4UGZn PersonRendererTest: https://goo.gl/uhcL2k Partie 5 Tests unitaires
  49. 49. Mockito : Initialisation CAS DE TEST TEST @Inject ManufacturerDetailPresenter( EventBus eventBus, MyView view, MyProxy proxy, ResourceDelegate<ManufacturersResource> manufacturers, PlaceManager placeManager, EditManufacturerMessages messages) { super(eventBus, view, proxy, SLOT_MAIN_CONTENT); this.manufacturers = manufacturers; this.placeManager = placeManager; this.messages = messages; getView().setUiHandlers(this); } public class ManufacturerDetailPresenterMockitoTest { // SUT private ManufacturerDetailPresenter presenter; // Mocks (created by Mockito) @Mock private EventBus eventBus; @Mock private MyView view; @Mock private MyProxy proxy; @Mock private ResourceDelegate<ManufacturersResource> manufacturers; @Mock private PlaceManager placeManager; @Before public void setUp() { // Create @Mock and @Captor fields. MockitoAnnotations.initMocks(this); // Manual way to create a mock EditManufacturerMessages messages = mock(EditManufacturerMessages.class); // Manual creation of the SUT. presenter = new ManufacturerDetailPresenter(eventBus, view, proxy, manufacturers, placeManager, messages); }
  50. 50. Mockito : Test CAS DE TEST TEST @Override protected void onReveal() { List<ActionType> actions; if (createNew) { actions = Arrays.asList(ActionType.DONE); } else { actions = Arrays.asList(ActionType.DELETE, ActionType.UPDATE); } ChangeActionBarEvent.fire(this, actions, false); } @Test public void onReveal() { // when presenter.onReveal(); // then // Manual way to create an argument captor ArgumentCaptor<ChangeActionBarEvent> captor = ArgumentCaptor.forClass(ChangeActionBarEvent.class); verify(eventBus) .fireEventFromSource(captor.capture(), same(presenter)); ChangeActionBarEvent event = captor.getValue(); assertThat(event).isNotNull(); assertThat(event.getActions()).containsOnly(ActionType.DONE); assertThat(event.getTabsVisible()).isFalse(); }
  51. 51. Jukito : Initialisation CAS DE TEST TEST @RunWith(JukitoRunner.class) public class ManufacturerDetailPresenterTest { // SUT (Injected by Jukito). We don't need to create all dependencies explicitly, Jukito will mock them. @Inject private ManufacturerDetailPresenter presenter; // Mocks (injected by Jukito) @Inject private EventBus eventBus; // Captors (Create by Mockito) @Captor private ArgumentCaptor<ChangeActionBarEvent> changeActionBarEventCaptor; @Before public void setUp() { // Create @Mock and @Captor fields. MockitoAnnotations.initMocks(this); } @Inject ManufacturerDetailPresenter( EventBus eventBus, MyView view, MyProxy proxy, ResourceDelegate<ManufacturersResource> manufacturers, PlaceManager placeManager, EditManufacturerMessages messages) { super(eventBus, view, proxy, SLOT_MAIN_CONTENT); this.manufacturers = manufacturers; this.placeManager = placeManager; this.messages = messages; getView().setUiHandlers(this); }
  52. 52. Jukito : Test CAS DE TEST TEST @Override protected void onReveal() { List<ActionType> actions; if (createNew) { actions = Arrays.asList(ActionType.DONE); } else { actions = Arrays.asList(ActionType.DELETE, ActionType.UPDATE); } ChangeActionBarEvent.fire(this, actions, false); } @Test public void onReveal_newManufacturer_preparesActionBar() { // when presenter.onReveal(); // then verify(eventBus) .fireEventFromSource(changeActionBarEventCaptor.capture(), same(presenter)); ChangeActionBarEvent event = changeActionBarEventCaptor.getValue(); assertThat(event).isNotNull(); assertThat(event.getActions()).containsOnly(ActionType.DONE); assertThat(event.getTabsVisible()).isFalse(); }
  53. 53. Jukito : Utiliser @All pour tester plusieurs cas CAS DE TEST TEST @Inject PersonRenderer( Messages messages, @Assisted Mode mode) { this.mode = mode; this.messages = messages; } public String render(Person person) { String result = ""; if (person != null) { String firstName = person.getFirstName(); String middleName = person.getMiddleName(); String lastName = person.getLastName(); String title = renderTitle(person); if (mode.isDisplayTitle() && !Strings.isNullOrEmpty(title)) { result += title + " "; } if (mode.isDisplayFirstName() && !Strings.isNullOrEmpty(firstName)) { result += firstName + " "; } if (mode.isDisplayMiddleName() && !Strings.isNullOrEmpty(middleName)) { result += middleName + " "; } if (mode.isDisplayLastName() && !Strings.isNullOrEmpty(lastName)) { result += lastName; } } if (result.isEmpty()) { result = messages.unknown(); } return result; } public class PersonNameTestCase { private final Person person; private Mode mode; private String expected; public PersonNameTestCase() { person = null; } public PersonNameTestCase( String firstName, String lastName) { person = new Person(firstName, lastName); } /* Setters */ public Person getPerson() { return person; } public Mode getMode() { return mode; } public String getExpected() { return expected; } }
  54. 54. Jukito : Utiliser @All pour tester plusieurs cas (suite) TEST TEST @RunWith(JukitoRunner.class) public class PersonRendererTest { public static class Module extends JukitoModule { @Override protected void configureTest() { bindManyInstances(PersonNameTestCase.class, new PersonNameTestCase("Zom", "Bee") .mode(Mode.SHORT).expected("Zom Bee"), new PersonNameTestCase("Zom", "Bee").title(Title.MR) .mode(Mode.SHORT).expected("Zom Bee"), new PersonNameTestCase("Zom", "Bee").middleName("Buzz") .mode(Mode.SHORT).expected("Zom Bee"), new PersonNameTestCase("Zom", "Bee").title(Title.MR).middleName("Buzz") .mode(Mode.SHORT).expected("Zom Bee"), new PersonNameTestCase("Zom", "Bee") .mode(Mode.FORMAL).expected("Zom Bee"), new PersonNameTestCase("Zom", "Bee").title(Title.MS) .mode(Mode.FORMAL).expected("Ms. Zom Bee"), new PersonNameTestCase("Zom", "Bee").middleName("Buzz") .mode(Mode.FORMAL).expected("Zom Buzz Bee"), new PersonNameTestCase("Zom", "Bee").title(Title.MS).middleName("Buzz") .mode(Mode.FORMAL).expected("Ms. Zom Buzz Bee"), new PersonNameTestCase() .mode(Mode.SHORT).expected(UNKNOWN), new PersonNameTestCase() .mode(Mode.FORMAL).expected(UNKNOWN) ); } } private static final String UNKNOWN = "unknown"; @Inject private Messages messages; @Before public void setUp() { given(messages.unknown()).willReturn(UNKNOWN); given(messages.title(Title.MR)).willReturn("Mr."); given(messages.title(Title.MS)).willReturn("Ms."); } @Test public void render(@All PersonNameTestCase testCase) { // given Person person = testCase.getPerson(); Mode mode = testCase.getMode(); PersonRenderer renderer = new PersonRenderer(messages, mode); // when String result = renderer.render(person); // then String expected = testCase.getExpected(); assertThat(result).overridingErrorMessage( "Expected %s with mode %s to be '%s'.", person, mode, expected) .isEqualTo(expected); }
  55. 55. Jukito : Utilitaires pour tester Rest-Dispatch CAS DE TEST TEST public void onSave(ManufacturerDto manufacturer) { manufacturersDelegate .withCallback(new AbstractAsyncCallback<ManufacturerDto>(this) { @Override public void onSuccess(ManufacturerDto newManufacturer) { DisplayMessageEvent.fire(ManufacturerDetailPresenter.this, new Message(messages.manufacturerSaved(), SUCCESS)); PlaceRequest placeRequest = new Builder() .nameToken(NameTokens.MANUFACTURER) .build(); placeManager.revealPlace(placeRequest); } }) .saveOrCreate(manufacturerDto); } @Test public void onSave_showsMessage_revealsManufacturers( EditManufacturerMessages messages) { // given ManufacturerDto manufacturer = new ManufacturerDto(); ManufacturerDto resultManufacturer = new ManufacturerDto(); givenDelegate(manufacturersResource) .succeed().withResult(resultManufacturer) .when().saveOrCreate(same(manufacturer)); given(messages.manufacturerSaved()).willReturn(A_MESSAGE); // when presenter.onSave(manufacturer); // then // note `isA` is used instead of `any`. This is because the event bus // accepts all `GwtEvent` subclasses. `isA` also verifies the type verify(eventBus) .fireEventFromSource(isA(DisplayMessageEvent.class),same(presenter)); verify(placeManager).revealPlace(placeRequestCaptor.capture()); PlaceRequest placeRequest = placeRequestCaptor.getValue(); assertThat(placeRequest.getNameToken()) .isEqualTo(NameTokens.MANUFACTURER); }
  56. 56. GWT Meilleures Pratiques Partie 6
  57. 57. Avoid widgets as much as you can BEST PRACTICE 1 Partie 6
  58. 58. WIDGETS ARE HEAVY. BECAUSE Partie 6
  59. 59. TAKE CONTROL OF YOUR HTML!
  60. 60. Full Event Mechanism BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN Partie 6
  61. 61. Even if you don’t need it Full Event Mechanism BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN Partie 6
  62. 62. How to attach event handler to elements ? BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN Partie 6
  63. 63. Use widget To encapsulate complex component to reuse » prefer PresenterWidget if the component has a lot of business logic » In the futur: Web Component. BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN Partie 6
  64. 64. Cell widgets (CellTable, CellList…) HtmlPanel Exceptions BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN Partie 6
  65. 65. Use an Event Bus BEST PRACTICE 2 Partie 6
  66. 66. Use Dependency Injection BEST PRACTICE 3 Partie 6
  67. 67. Use MVP Pattern BEST PRACTICE 4 Partie 6
  68. 68. Make Navigation Easy BEST PRACTICE 5 Partie 6
  69. 69. Use directly your name tokens BP 5 : MAKE NAVIGATION EASY Partie 6
  70. 70. Use CSS as much as you can BEST PRACTICE 6 Partie 6
  71. 71. Loas everything you can in the html page BEST PRACTICE 7 Partie 6
  72. 72. UNIT TESTS Partie 6
  73. 73. Test each protected and public methods of your presenters. UNIT TESTS Partie 6
  74. 74. CODE REVIEW Partie 6
  75. 75. Improve quality of your code CODE REVIEW Partie 6
  76. 76. Detect Bugs earlier CODE REVIEW Partie 6
  77. 77. A way to learn from your peers CODE REVIEW Partie 6
  78. 78. Web based code review system CODE REVIEW Partie 6
  79. 79. CODE REVIEW Best practices Partie 6
  80. 80. TAKE CONTROL OF YOUR HTML!USE A STYLE GUIDE !
  81. 81. Address/fix all comments before you merge. CODE REVIEW Best Practice Partie 6
  82. 82. CONTINUOUS INTEGRATION Partie 6
  83. 83. Partie 6
  84. 84. The first to do code review is your CI Server CONTINUOUS INTEGRATION Partie 6
  85. 85. Partie 6
  86. 86. Enforce Check Style CONTINUOUS INTEGRATION Partie 6
  87. 87. ACTORS Partie 6
  88. 88. Partie 6
  89. 89. Partie 6
  90. 90. Designer Partie 6
  91. 91. Web Integrators Partie 6
  92. 92. Developpers Partie 6
  93. 93. Partie 6
  94. 94. Partie 6
  95. 95. Partie 6
  96. 96. Partie 6
  97. 97. SUCCESS Partie 6
  98. 98. GWT 3.0 Partie 7
  99. 99. Java 8 Support Partie 7
  100. 100. JSInterop Partie 7
  101. 101. Export any Java code to Javascript JSinterop Partie 7
  102. 102. Export any Javascript API to Java Partie 7 JSinterop
  103. 103. Subclassing JavaScript Objects Partie 7 JSinterop
  104. 104. Web component Material design in GWT Partie 7 JSinterop
  105. 105. Start to experiment with GWT 2.7 » Turn it on with a flag -XjsInteropMode » Subject to change ! Partie 7 JSinterop
  106. 106. MERCI
  107. 107. QUESTIONS ?

×