Recherche

Coder's IO

Auteur

Fabrice Sznajderman

Développeur senior Scala/Web.

Run a Lagom service standalone with Zookeeper

We know that Lagom doesn’t prescribe any particular environment, although Lagom provide out of the box ConductR, an integrated environment that provide several services to deploy and manage your services. It would be interesting, in some cases, to be able to deploy a service in standalone fashion.

This article start with idea that you know how write and run a Lagom service in dev environment.

We know that one microservice cannot live without service locator. In development environment, Lagom’s plugin provided one service locator out of the box. It exclusively dedicated to the dev mode. The unique thing required to run a Lagom microservice in standalone mode is a service Locator.

Here we will see how run a Lagom service in standalone mode.

To follow this guide, this imply you have one Zookeeper instance accessible. Please look at here to learn how running zookeeper instance

As service locator registry, we will used Zookeeper. For that, we will use the James Boner’s project that implements service Locator interface of Lagom for Zookeeper.

This guide will explain how to :

  • integrate the zookeeper service locator in your Lagom’s project
  • package a service to run it in standalone manner

We will use the following service implementation :

Service interface

import com.lightbend.lagom.javadsl.api.Descriptor;
import com.lightbend.lagom.javadsl.api.Service;
import com.lightbend.lagom.javadsl.api.ServiceCall;

import static com.lightbend.lagom.javadsl.api.Service.named;
import static com.lightbend.lagom.javadsl.api.transport.Method.POST;

public interface HelloWorldService extends Service {

    ServiceCall<String, String> sayHello();

    @Override
    default Descriptor descriptor() {

        return named("helloWorld").withCalls(Service.restCall(POST, "/msg&amp", this::sayHello)).withAutoAcl(Boolean.TRUE);
    }
}

Service implementation

import com.lightbend.lagom.javadsl.api.ServiceCall;

import static java.util.concurrent.CompletableFuture.completedFuture;

public class HelloWorldServiceImpl implements HelloWorldService {

    @Override
    public ServiceCall<String, String> sayHello() {
        return request -> completedFuture("Hello" + request );
    }
}

 

How integrate the zookeeper service locator in your Lagom’s project

First step will be to add an implementation of the zookeeper service locator into your Lagom service.
For that, we have to build locally the lagom-zookeeper-service-locator project.
We have to do that because the project is under snapshot version.
Well, clone the project with the command below :

git clone https://github.com/jboner/lagom-service-locator-zookeeper.git

then publish locally the project as below :

 > sbt publishLocal

Once is published, we have to add the project as dependencies in your service, open your build.sbt file et add line as below :

 libraryDependencies in ThisBuild += "com.lightbend.lagom" % "lagom-service-locator-zookeeper_2.11" % "1.0.0-SNAPSHOT"

Now the service locator must be activate. We have to declare the module’s class in the service configuration file (by default, application.conf). Informations below indicated to the service Locator how reach zookeeper instance :

lagom {
  discovery {
    zookeeper {
      server-hostname = "127.0.0.1"   # hostname or IP-address for the ZooKeeper server
      server-port     = 2181          # port for the ZooKeeper server
      uri-scheme      = "http"        # for example: http or https
      routing-policy  = "round-robin" # valid routing policies: first, random, round-robin
    }
  }
}

Here, your service is able to work with the service locator but it is not yet localizable. We going to modify the service module class to ensure that the service becomes localizable.

public class HelloWorldServiceModule extends AbstractModule implements ServiceGuiceSupport {

    private Environment environment;

    @Inject
    public HelloWorldServiceModule(Environment environment, Configuration configuration) {
        this.environment = environment;
    }

    @Override
    protected void configure() {

        bindServices(serviceBinding(HelloWorldService.class, HelloWorldServiceImpl.class));

        if (environment.mode() == Mode.Prod()) {

            try {
                ZooKeeperServiceRegistry registry = new ZooKeeperServiceRegistry(
                        ZooKeeperServiceLocator.zkUri(),
                        ZooKeeperServiceLocator.zkServicesPath());
                registry.start();

                // create the service instance for the service discovery
                // needs to be held on to to be able to unregister the service on shutdown
                ServiceInstance<String> serviceInstance = ServiceInstance.<String>builder()
                        .name("helloWorld")
                        .id("helloWorldId")
                        .address("localhost")
                        .port(8080)
                        .uriSpec(new UriSpec("{scheme}://{serviceAddress}:{servicePort}"))
                        .build();

                // register the service
                registry.register(serviceInstance);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

The code above, record the service reference into zookeeper. You noticed that the code will be enabled only in production environment to avoid any conflict with service locator from dev environment. name and id parameters need a little explanation. Name parameter is the name os the microservice while id parameter is the reference of the instance of the microservice.

Now your service is ready to be used in standalone manner. Next, we will see how packaged and run the service.

How package a service to run in standalone manner

This step is very simple. To package your service, from sbt console you should select service implementation project by the command :

> project helloWorld-imp

Once, it is selected, simply run the following command :

 > dist

Then to start your service in standalone manner, go into the following path:

/helloworld/helloWorld-impl/target/universal/

Here, you will find an archive named by the service implementation name, in our case helloworldimpl-[current_version].zip
In our case, name should be

helloworldimpl-1.0-SNAPSHOT.zip

This archive contains all elements require to run in standalone manner.

Unzip archive :

unzip helloworldimpl-1.0-SNAPSHOT.zip

and finally, run the script localized in bin directory :

 ./helloworldimpl-1.0-SNAPSHOT/bin/helloworldimpl

Now, your Lagom service started in standalone manner. It can be interact with all your others services.

If you connect to your zookeeper instance by the zookeeper client (zkCli.sh). With the following command you will get all service’s information (path corresponds to the configuration that we put into class’ module) :

get /lagom/services/helloWorld/helloWorldId

Result that you will obtain :

{"name":"helloWorld","id":"helloWorldId","address":"localhost",
"port":8080,"sslPort":null,"payload":null,
"registrationTimeUTC":1474744298139,"serviceType":"DYNAMIC",
"uriSpec":{"parts":[{"value":"scheme","variable":true},
{"value":"://","variable":false},
{"value":"serviceAddress","variable":true},
{"value":":","variable":false},{"value":"servicePort","variable":true}]}}

This configuration should be add in all yours services that compose your microservices system. After, your services can localize all others services through the service locator.

I hope this article will give you a clear understanding of relation between Lagom service and service Locator.

Lagom with Scala for impatients

Do you hear about Lagom framework? It is a framework that eases you development of system of microservices based on the reactive principles. You can find more details about it here.

Although the core of Lagom has been written in Scala, the framework API is proposed in Java. The Java API is nice and provides a good experience for the developer but, as a Scala developer, i am really impatient to play with the Scala API. This version of API is planned to be available soon (there is one dedicated issue on the github of project)

Well, during the waiting time, you can start to develop your service based on Scala language. To do this, You have to used one hack that you will allow to work with Scala from now.

The purpose of this article is to explain what you have to do! 🙂

Hack is based on implicit conversion of the Scala type into the Java type. This conversion will keep the compliant with the Java API.

To avoid long explanations, I will show you an example of code. Example is based on Lagom project that available here. It is written by Mirco Dotta, one of the lead developer on the Lagom project.

This article assumes that you are familiar with the Lagom architecture and all classes that are involved in one service.

First of all, we are starting with interface of service, below implementation :

import akka.stream.javadsl.Source

import akka.NotUsed
import com.lightbend.lagom.javadsl.api.Descriptor
import com.lightbend.lagom.javadsl.api.ScalaService._
import com.lightbend.lagom.javadsl.api.Service
import com.lightbend.lagom.javadsl.api.ServiceCall

trait MyScalaService extends Service {

def serviceSample(): ServiceCall[NotUsed, String]

override def descriptor(): Descriptor = {
named("MyScalaService")
.withCalls(namedCall("/api/sample/", serviceSample _))
.withAutoAcl(true)
}
}

Where we implement an interface in Java, here we provide one Trait. It extend the Service’s interface as we do it in Java. The main thing different, except syntax, it is the import of the object ScalaService
This object provide several implicit conversion as you can see here

Now, we’re looking for the implementation part:

class MyScalaServiceImpl @inject() ()(implicit ex: ExecutionContext) extends MyScalaService {

import converter.ServiceCallConverter._

override def serviceSample(): ServiceCall[NotUsed, String] = {
notUsed => {
Future.successful("Hello world")
}
}

Overall, there is no fundamental difference with Java approach.
However, you have to take care about few details.
– you must put the @inject() annotation on the constructor.
– the curried constructor where the second parameter define an implicit parameter required by the class Future.
– add the following import :

import converter.ServiceCallConverter._

This import provided some implicits conversion that are useful for the compliance with the Java Api. Those converters translate type Future to type CompletionStage (and reversely), below the (non standard) implementation found in the project sample here.
You have to used it, or implement your version.
– And in the module’s definition of service implementation you have to add an option on settings :

scalacOptions in Compile += "-Xexperimental"

This option enables Scala lambdas to be passed as Java SAMs more details here

Remember, this article describe one hack to write your service based on Scala language. Today, the Lagom’s Scala API is not available yet. For this sample, we are using many implicit conversion to achieve this goal

The last thing that I have to show you, how write the module (that aim to bind interface and implementation class). Here also, the difference will subtle :

import com.google.inject.AbstractModule
import com.lightbend.lagom.javadsl.server.ServiceGuiceSupport

import MyScalaService

class MyScalaServiceModule extends AbstractModule with ServiceGuiceSupport {

protected override def configure(): Unit =
bindServices(serviceBinding(classOf[MyScalaService], classOf[MyScalaServiceImpl]))
}

In summary, if you follow steps described above, you can enable Scala language in Lagom.

To help you to start after reading this article you could follow one of the three links below (or all if you want :D).

  1. A helloworld project sample that is written in with Scala language and contains just the minimal code that is described above Helloworld-scala-lagom
  2. This project contains more. It has been written by Mirco Dotta as example. activator-lagom-scala-chirper 
  3. Here this is a plugin that eases the generation of Lagom service. It can generate the service in Java and in Scala (as described below) : scaffolding-plugin-lagom

Feel free to ask me any questions you may have about details.

Lagom : Architecture micro-service réactive

Au mois de mars, Lightbend (ex Typesafe) sortait son framework de micro service. Un de plus me direz-vous? oui peut être.. mais, car il y a toujours un mais 🙂 Ce framework propose quelques petites choses intéressantes. Je vous propose dans ce cours article de faire le point!

Le créateur du Lagom, *Jonas Boner*, met en avant les points suivants pour parler de son framework et décrire les différenciateurs par rapport à l’écosystème existant :

  •  par défaut la communication est asynchrone entre chaque service. Evidemment, il est possible de faire du synchrone également si nécessaire.
  •  côté persistence, le framework se base sur les patterns d’Event sourcing et CQRS. Par défaut.
  •  Lagom offre un environnement de développement ou l’on peut travailler efficacement. C’est-à-dire que l’on peut démarrer l’ensemble de ses microservices à l’aide d’une seule commande. Au lancement, Lagom lance une base Cassandra (embeded, un service locator et un service gateway, par défaut.
  • Lagom supporte le rechargement à chaud du code après modification.

*(liste non exhaustive)*

D’une manière générale, Lagom a été bati selon les principes réactifs (décrit dans le reactive manifesto.

D’un point de vue technique, la première version de Lagom est développée sur une base Java. Néanmoins, lorsque l’on rentre dans les entrailles de la bête, on croise du code Scala (ouf ! 🙂 ).

Prochainement, une version Scala sera proposée.

Lagom met en oeuvre pas mal de librairies externes pour adresser les différentes problématiques techniques. Voici une liste des librairies que l’on peut trouver :

  • Immutable > Objet immuable
  • Play framework > Web
  • SBT > Build
  • Guice > IOC
  • Akka > Clustering, message, Stream
  • SLF4J/LogBack > Logging
  • Jackson > Sérialisation
  • Cassandra > Stockage
  • ConductR > Déploiement, service locator

J’ai commencé à jouer avec pour découvrir et comprendre la philosophie de l’outil. Comme on peut le voir, beaucoup de concepts et de composants techniques sont utilisés, ce qui rend l’environnement riche et intéressant.

Une fois le tour du propriétaire fait, on se rend compte que l’écriture et le déploiement d’un service sont très simple.

Le principal concurrent de ce framework serait SpringBoot.

Actuellement, la documentation est vraiment centrée sur l’essentiel et on ne trouve pas tout; mais il est facile d’obtenir des informations notamment au chat mis en place sur Gitter ou l’on peut discuter facilement avec les contributeurs du projet.

Le projet est accessible ici. Si le sujet vous intéresses, je vous recommande d’y jeter un oeil.

Scala : equals vs ==

EqualitySign

De quoi parle t’on?

Il est parfois nécessaire de comparer deux instances afin de vérifier si celle-ci sont équivalentes. La notion d’équivalence peut se voir sous différent angle.

On peut souhaiter comparer les deux instances afin de savoir s’il s’agit de la même instance ( même référence en mémoire) ou alors vérifier si d’un point de vue fonctionnel les deux instances comparées représentent la même entité fonctionnelle. Nous allons voir dans cet article comment ça se passe en Scala. Avant d’aborder l’approche Scala nous ferons un rappel sur ce qui se passe en Java.

 

Petit rappel

En Java, chaque classe hérite de deux méthode permettant d’une part comparer deux objets et d’autre part d’optimiser la recherche d’une instance stockée dans une collection.

Les deux méthodes en question sont les méthodes equals() et  hashcode().

L’implémentation par défaut est fournie par le type Object, classe parente de toutes les classes du JDK.

 

Exemple

Prenons comme exemple la classe Person :

public class Person {

 private String name;
 private int age;


 public Person(String name, int age) {
  this.name = name;
  this.age = age;
 }

 public String getName() {
  return name;
 }

 public int getAge() {
  return age;
 }
}

J’utilise la librairie FunSuite de ScalaTest pour écrire mes tests, je trouve qu’elle est plus lisible et plus pratique à utiliser

Le test suivant vérifie bien que p1 et p2 ne sont pas égaux :

test("p1 et p2 are not equals") {
 val p1 = new Person("Louis", 10)
 val p2 = new Person("Louis", 10)

 p1.equals(p2)

}

Par défaut, en Java, la méthode equals() va comparer les références des deux objets. Si l’on souhaite faire une comparaison fonctionnelle des deux instances (basée sur la représentation interne de l’objet, en l’occurrence l’attribut name et age) il faut redéfinir les méthodes la méthode equals ainsi que la méthode hashcode().

Les règles de redéfinition

Pour que ce rappel soit complet, il faut énoncer les règles à suivre pour redéfinir ces deux méthodes.

Elle doit être :

  • Symétrique : pour deux références a et b, si a.equals(b) alors il faut obligatoirement que b.equals(a)

  • Réflexive : pour toute référence non null, a.equals(a) doit toujours renvoyer true

  • Transitive : si a.equals(b) et b.equals(c) alors a.equals(c)

  • Consistante avec la méthode hashCode() : si deux objets sont égaux en invoquant la méthode equals() alors leur méthode hashCode() doit renvoyer la même valeur pour les deux objets

  • Pour toute référence non null, a.equals(null) doit toujours renvoyer false

(Référence : Class Object – JavaDoc )

Exemple de redéfinition pour la classe Person

A partir de la plupart des IDEs, il est possible de générer le code  automatiquement des deux méthodes. Ci-dessous, un exemple de génération à parti d’IntelliJ :

@Override
public boolean equals(Object o) {
 if (this == o) return true;
 if (o == null || getClass() != o.getClass()) return false;

 Person person = (Person) o;

 if (age != person.age) return false;
 if (name != null ? !name.equals(person.name) : person.name != null) return false;

 return true;
}

@Override
public int hashCode() {
 int result = name != null ? name.hashCode() : 0;
 result = 31 * result + age;
 return result;
}

 

En Java, ça se passe comme ça

Suite à la redéfinition  de ces deux méthodes on va pouvoir comparer d’un point de vue fonctionnel les deux instances.

Si l’on exécute à nouveau le test précédent alors celui-ci se finira avec succès. En effet, en java, la méthode equals  permet d’effectuer une comparaison fonctionnelle tandis que l’opérateur == va seulement comparer les références mémoires des deux objets.

Soit les deux instances suivantes de la classe Person :

test("p1 et p2 are not ==") {
 val p1 = new Person("Louis", 10)
 val p2 = new Person("Louis", 10)

 p1 == p2 // false
}


test("p1 et p2 are equals") {
 val p1 = new Person("Louis", 10)
 val p2 = new Person("Louis", 10)

 p1.equals(p2) //true
}

En Scala, ça se passe autrement

En Scala, le comportement est différent. L’opérateur == s’appuie sur la méthode  equals pour effectuer la comparaison.

Par conséquent, tant que la méthode equals n’a pas été redéfinie, alors l’opérateur == fera une comparaison sur la référence des objets.

Une fois la méthode redéfinie, l’opérateur fera une comparaison fonctionnelle.

Dans du code, l’utilisation du ==, à la place de equals est plus naturel.

Dans le cas de notre classe Person le comportement des tests sera le suivant :

test("p1 et p2 are not ==") {
 val p1 = new Person("Louis", 10)
 val p2 = new Person("Louis", 10)

 p1 == p2 // true
}

test("p1 et p2 are equals") {
 val p1 = new Person("Louis", 10)
 val p2 = new Person("Louis", 10)

 p1.equals(p2) //true
}

Vous me direz mais comment je peux faire une comparaison par référence après redéfinition de la méthode equals ?? Et bien je vous répondrais que Scala met à disposition (via la superclasse AnyRef) deux méthodes

  • eq : égalité entre deux objets par rapport à leurs références
  • ne : équivalent à !(a eq b).

Conclusion

Vous me direz c’est bien étrange d’avoir fait ce choix par rapport à Java. Si on y regarde de plus près, je trouve que l’utilisation de l’opérateur == pour effectuer des comparaisons métiers n’est pas si mauvaise que cela. A l’écriture du code ça parait plus naturel et le résultat apporte de la concision au code en restant très lisible.

Quand on vient du monde Java, ça peut être un peu déstabilisant mais au final lorsque l’on sait comment ça fonctionne, l’approche parait plus naturelle et les habitudes viennent rapidement.

 

 

 

Kata : GildedRose-Refactoring(Scala)

Qui Que Quoi Ou?

Je me suis lancé récemment dans le kata GildedRose-Refactoring.
Bien que le kata soit connu, un petit rappel s’impose. L’idée de ce défi est de refactoriser une méthode extraite d’une vaste application de gestion d’item. Avec le bout de code, on a un embryon de spécification.

Dans la spécification on nous précise que le développeur à l’origine de ce code est parti pour de nouvelles aventures et qu’il ne reste que le code dont je passerai sous silence la qualité. Bien entendu, aucun test. Eh oui, la devise du développeur précédent : Tester c’est douter et franchement vu le code, il ne doutait de rien :).

Ce kata est proposé par Emily Bache sur son dépôt github. L’exercice est disponible dans plusieurs languages dont Scala que j’ai choisi pour faire le challenge.

Bon rentrons dans le vif du sujet 🙂

Dans ce post, je vous présente le résultat du refactoring que j’ai réalisé en faisant le kata. J’expliquerai les choix faits dans la solution. N’hésitez pas à remonter vos remarques dans les commentaires.

Base de travail

Voici le code à partir duquel je vais travailler :

package com.gildedrose

class GildedRose(val items: Array[Item]) {


  def updateQuality() {
    for (i <- 0 until items.length) {
      if (!items(i).name.equals("Aged Brie")
        && !items(i).name.equals("Backstage passes to a TAFKAL80ETC concert")) {
        if (items(i).quality > 0) {
          if (!items(i).name.equals("Sulfuras, Hand of Ragnaros")) {
            items(i).quality = items(i).quality - 1
          }
        }
      } else {
        if (items(i).quality < 50) {
          items(i).quality = items(i).quality + 1

          if (items(i).name.equals("Backstage passes to a TAFKAL80ETC concert")) {
            if (items(i).sellIn < 11) {
              if (items(i).quality < 50) {
                items(i).quality = items(i).quality + 1
              }
            }

            if (items(i).sellIn < 6) {
              if (items(i).quality < 50) {
                items(i).quality = items(i).quality + 1
              }
            }
          }
        }
      }

      if (!items(i).name.equals("Sulfuras, Hand of Ragnaros")) {
        items(i).sellIn = items(i).sellIn - 1
      }

      if (items(i).sellIn < 0) {
        if (!items(i).name.equals("Aged Brie")) {
          if (!items(i).name.equals("Backstage passes to a TAFKAL80ETC concert")) {
            if (items(i).quality > 0) {
              if (!items(i).name.equals("Sulfuras, Hand of Ragnaros")) {
                items(i).quality = items(i).quality - 1
              }
            }
          } else {
            items(i).quality = items(i).quality - items(i).quality
          }
        } else {
          if (items(i).quality < 50) {
            items(i).quality = items(i).quality + 1
          }
        }
      }
    }
  }
}

On notera une grande complexité avec l’imbrication des structures conditionnelles. Enfin un beau terrain de jeu en perspective 😉
La spécification est disponible ici

Les problèmes constatés

En lisant le code, j’ai détecté les problèmes suivants :

  • complexité dans le code (structures conditionnelles)
  • les règles métiers sont éparpillées et sont difficilement identifiables
  • utilisation de magic number
  • méthode contenant beaucoup trop de ligne de code

Couverture du code par les tests

La première étape a été d’ajouter les tests unitaires permettant de couvrir le code que j’allais refactorer. Cette ceinture de sécurité est indispensable pour commencer à modifier du code existant sans risquer d’introduire des bugs et régressions.

Pour créer ces tests, je me suis appuyé sur l’outil permettant de visualiser la couverture de test proposé par IntelliJ. Cet outil permet de visualiser graphiquement les lignes de code couvertes (ou pas) par les tests.
Au fur et à mesure que je crée des tests, je peux voir quelles sont les lignes de code impactées par mon code.

A partir de la spec. et du code existant j’ai écrit les tests suivants :


package com.gildedrose

import org.scalatest._

class GildedRoseTest extends FlatSpec with Matchers {
  "item(s)" should "foo" in {
    val items = Array[Item](Item("foo", 0, 0))
    val app = new GildedRose(items)
    app.updateQuality()
    (app.items(0).name) should equal("foo")
  }

  it should "foo &amp; bar" in {
    val items = Array[Item](Item("foo", 0, 0),Item("bar", 0, 0))
    val app = new GildedRose(items)
    app.updateQuality()
    (app.items(0).name) should equal("foo")
    (app.items(1).name) should equal("bar")
  }

  "Quality" should "increased by one for Aged Brie &amp; Backstage passes to a TAFKAL80ETC concert " in {
    val items = Array[Item](Item("Aged Brie", 8, 15)
      , new Item("Backstage passes to a TAFKAL80ETC concert", 35, 20))
    val app = new GildedRose(items)
    app.updateQuality()
    (app.items(0).quality) should equal(16)
    (app.items(1).quality) should equal(21)

  }

  it should "increased by 2 for Backstage passes to a TAFKAL80ETC concert " in {
    val items = Array[Item](Item("Backstage passes to a TAFKAL80ETC concert", 10, 20))
    val app = new GildedRose(items)
    app.updateQuality()
    (app.items(0).quality) should equal(22)


  }

  it should "drops to 0 after the concert " in {
    val items = Array[Item](Item("Backstage passes to a TAFKAL80ETC concert", 0, 20))
    val app = new GildedRose(items)
    app.updateQuality()
    (app.items(0).quality) should equal(0)


  }
  it should "increased by 3 for Backstage passes to a TAFKAL80ETC concert " in {
    val items = Array[Item](Item("Backstage passes to a TAFKAL80ETC concert", 5, 20))
    val app = new GildedRose(items)
    app.updateQuality()
    (app.items(0).quality) should equal(23)
  }

  it should "decreased by two" in {
    val items = Array[Item](Item("foo", 0, 2))
    val app = new GildedRose(items)
    app.updateQuality()
    (app.items(0).quality) should equal(0)
  }

  "Sulfuras, Hand of Ragnaros" must  "never has to be sold or decreases in Quality" in {
    val items = Array[Item](Item("Sulfuras, Hand of Ragnaros", 0, 20))
    val app = new GildedRose(items)
    app.updateQuality()
    app.updateQuality()
    app.updateQuality()

    (app.items(0).quality) should equal(20)
    (app.items(0).sellIn) should equal(0)
  }

  "Aged Brie" should  "increases in Quality the older it gets" in {
    val items = Array[Item](Item("Aged Brie", 0, 20))
    val app = new GildedRose(items)
    app.updateQuality()
    app.updateQuality()
    app.updateQuality()

    (app.items(0).quality) should equal(26)
    (app.items(0).sellIn) should equal(-3)
  }
}

Refactoring, c’est parti!!

Démarrage du refactoring de la classe. En analysant le code, on peut s’apercevoir que l’on ne manipule des items précis, c’est-à-dire que chaque item à ses propres règles métiers pour la gestion de sa qualité. En voyant ça, ma première approche sera d’extraire les règles métiers de chaque item et de les isoler.

Dans cet exercice, on a quelques contraintes, la première étant que la classe Item ne doit pas être modifiée. La seconde moins explicite est que l’on touche à un sous-ensemble de l’application. Donc c’est plus difficile d’avoir une approche globale et de changer profondément l’architecture.

Pour réaliser cette isolation je vais utiliser les traits et une feature sympathique du langage : les implicits.
L’idée est de tenter d’abstraire de chaque item un sens un peu plus générique :

  • Aged Brie : FoodItem
  • Backstage passes to a TAFKAL80ETC concert : ConcertItem
  • Sulfuras, Hand of Ragnaros : LegendaryItem
  • Implémentation par défaut : DefaultItem

Chaque trait défini ci-dessus étendra un autre trait : ItemService, qui permet de définir le contrat qu’un item doit fournir. Dans chaque trait, je vais pouvoir définir les règles métiers correspondantes de la manière suivante :


trait ItemService {

    val item: Item

    def updateQuality() {}

    def updateSellin() {}

    def incQuality() = {
      if (item.quality < MAX_QUALITY) {
        item.quality += 1
      }
    }
  }

  trait ConcertItem extends ItemService {

    override def updateQuality() = {
      incQuality()

      if (item.sellIn < ELEVEN_STEP_SELLIN) {
        incQuality()
      }

      if (item.sellIn < SIX_STEP_SELLIN) {
        incQuality()
      }
    }

    override def updateSellin() = {
      item.sellIn = item.sellIn - 1
      if (item.sellIn < 0) {
        item.quality = 0
      }
    }
  }

  trait LegendaryItem extends ItemService {

  }

  trait FoodItem extends ItemService {

    override def updateQuality() = {
      incQuality()
    }

    override def updateSellin() = {
      item.sellIn = item.sellIn - 1

      if (item.sellIn < 0) {
        incQuality()
      }
    }
  }

  trait DefaultItem extends ItemService {

    override def updateQuality() = {
      if (item.quality > 0) {
        item.quality = item.quality - 1
      }
    }

    override def updateSellin() = {
      item.sellIn = item.sellIn - 1
      if (item.sellIn < 0) {
        item.quality = item.quality - 1
      }
    }
  }

Maintenant que les règles sont isolées il va falloir faire en sorte que chaque item soit lié à la bonne implémentation. Je rappelle que je ne peux pas modifier la classe Item. Pour lier, un item à l’implémentation qui lui correspond, je vais passer pas un wrapper qui va venir encapsuler l’item. pour cela je définis la case classe suivante :

case class Wrapper(item: Item)

Ce wrapper va me permettre de créer le lien entre l’item et le bon trait correspondant. Pour ce faire, je vais utiliser une factory qui à partir d’un item va me renvoyer un item wrappé avec la bonne implémentation. Ci-dessous la factory en question :

def convertToWrapper(item: Item): ItemService = {

    item.name match {
      case "Aged Brie" => new Wrapper(item) with FoodItem
      case "Backstage passes to a TAFKAL80ETC concert" => new Wrapper(item) with ConcertItem
      case "Sulfuras, Hand of Ragnaros" => new Wrapper(item) with LegendaryItem
      case _ => new Wrapper(item) with DefaultItem
    }

  }

Cette factory prend donc en paramètre un Item et nous renvoie une instance du type de trait le plus générique : ItemService.

Pour ceux qui ne connaissance pas Scala, je pense que l’encapsulation de l’item mérite quelques explications !

new Wrapper(item) with FoodItem

Le début de la ligne est clair, on crée une instance de la case classe wrapper en lui passant en paramètre l’item courant. Jusque-là pas de problème. C’est la deuxième partie qui s’obscurcit un peu.
Pour mémoire, un trait est approximativement la représentation d’une interface en java avec en plus la possibilité de fournir une implémentation par défaut pour les méthodes définies. Ceci étant dit, Scala permet d’appliquer le comportement d’un trait directement à l’instanciation d’une classe avec le mot-clef with.

Donc la conséquence de la ligne de code ci-dessus, permettra d’invoquer sur le wrapper les méthodes du trait FoodItem. Aussi comme il y a une relation d’héritage entre FoodItem et ItemService je peux coercer l’instance de la classe Wrapper dans le type ItemService (pour rendre le résultat de l’invocation de la factory complètement générique).

Maintenant chaque item porte ses propres règles métiers. A cet instant, le code de la méthode updateQuality() pourrait se résumer à ceci :


 def updateQuality() {
    items.foreach(item => {
      convertToWrapper(item) updateQuality()
      convertToWrapper(item) updateSellin()
    })
  }

Et les implicits alors?

En introduction, j’ai évoqué l’utilisation d’implicit. En effet, on peut aller un peu plus loin et réduire encore un peu plus le code ci-dessus. Pour cela, on va utiliser le mécanisme des implicits.

Pour rappel, implicit est un mécanisme permettant d’ajouter des comportements à des types de manière dynamique (entre autres je ne vais pas approfondir ici le concept en matière d’explication).

Pour utiliser le mécanisme, il me suffit d’ajouter de la méthode convertToWrapper le mot-clef implicit de la façon suivante :

implicit def convertToWrapper(item: Item): ItemService = {

    item.name match {
      case "Aged Brie" => new Wrapper(item) with FoodItem
      case "Backstage passes to a TAFKAL80ETC concert" => new Wrapper(item) with ConcertItem
      case "Sulfuras, Hand of Ragnaros" => new Wrapper(item) with LegendaryItem
      case _ => new Wrapper(item) with DefaultItem
    }

  }

En mettant implicit à cet endroit, j’indique au compilateur que s’il rencontre un type Item dans le scope de cet implicit, alors qu’il lui applique la méthode convertToWrapper. Cela aura pour effet que sur une instance de la classe Item je pourrai invoquer les méthodes définies par le trait Itemservice et par extension dans le trait correspondant à l’item manipulé.

L’impact au niveau du code donne le résultat suivant :

 def updateQuality() {
    items.foreach(item => {
      item updateQuality()
      item updateSellin()
    })
  }

Implémentation finale


package com.gildedrose


import com.gildedrose.ItemFactory._

class GildedRose(val items: Array[Item]) {

  def updateQuality() {

    items.foreach(item => {
      item updateQuality()
      item updateSellin()
    })
  }
}

Finalement, l’ajout d’un nouvel item passera par la modification de la classe de factory et éventuellement l’ajout d’un nouveau trait. Sur le long terme, cette solution ne serait pas satisfaisante et il faudrait envisager une refonte plus élargie du système (cf remarques)

Remarques

Dans cette nouvelle implémentation, on pourra remarquer que l’OpenClose principle n’est pas respecté. Dans ce kata, on n’a pas la main sur l’ensemble de l’application, donc avec les règles imposées il était difficile de respecter ce principe (très important). Je trouve que la solution proposée est assez élégante malgré la dérogation à ce principe.

L’utilisation des implicites doit être maîtrisée. La portée d’un implicit doit toujours être minimale au possible sous peine de gros ralentissement au moment de la compilation.

Conclusion

Le code résultant du refactoring est disponible sur mon github ici
Ce kata était intéressant à faire, si vous avez des remarques (constructives), n’hésitez pas à la mettre en commentaire.

Livre : Learning Spark

Auteurs : Holden Karau, Andy Konwiski, Patrik Wendell, Matei Zaharia
Pendant ces derniers mois j’ai suivi pas mal de MOOC sur des sujets liés au Big Data. Le dernier en date était une introduction au Big Data avec Spark. Pour aller plus loin, je me suis acheté le livre Learning Spark afin d’approfondir le sujet.

Je n’ai pas encore tout à fait terminé la lecture de l’ouvrage, mais je peux déjà donner mes impressions sur les trois quart du livre.

Le livre est très bien, il est un bon complément à cours en ligne que j’ai suivie. Les premiers chapitres du livre se concentrent sur les concepts fondamentaux de Spark. Cette première partie permet de fixer les notions vue d’un point de vue pratique pendant le cours.

Ensuite ce sont les problématiques de chargement et de sauvegarde de données. C’est un point essentiel naturellement car sans données Spark ne sert pas à grand chose :P.

Le chapitre suivant est consacré à la programmation avancée avec Spark. On découvre des tips de programmation que l’on peut mettre en oeuvre selon certains de use cases.

Lorsque l’on arrive à ce moment du livre, on a déjà une vision très claire de comment développer avec Spark.

Spark est un outil permettant de traiter massivement des données dans un environnement distribué. La deuxième partie du livre commence en abordant les capacités de Spark à fonctionner dans un environnement clusterisé et également s’intégrer avec des outils de déploiement comme Meso ou Yarn.

Le livre continue son exploration en abordant les problématiques de configuration avancées et la façon de faire du debugging de programme écrit avec Spark.

La dernière partie du livre est consacrée à la présentation de 3 modules de Spark. Pour l’instant seul le module principal : Spark Core avait été abordé.

Les 3 modules sont

  • Spark SQL : Librairie permettant de requêter des sources de données en se basant sur la – -language SQL.
  • Spark Streaming : Librairie permettant de traiter des flux en temps réel
  • Spark MLib : Librairie dédiée au Machine Learning
    Le tour d’horizon proposé par l’ouvrage donne une vision très claire sur Spark. Le spectre couvert est large et approfondi et permet de démarrer très rapidement un projet.

Bien que l’outil soit écrit en Scala, l’outil est polyglotte. Il est possible d’écrire ses programmes sur la base de 3 langages :

  • Scala
  • Python
  • Java

Etant complètement partial… Je préfère écrire mes programmes en Scala 😉 plus concis, expressif, etc… (troll ;))

En résumé, un très bon ouvrage qui se lit très bien. Pas de prérequis nécessaire pour lire ce livre. Je recommande se livre à qui veut se plonger dans l’univers de Spark!

Suite à la lecture de ce livre, j’ai commandé le livre : Advanded Analytics with Spark pour aller plus loin.

The Processing Instruction Target Matching « [xX][mM][lL] » is Not Allowed

J’ai récupéré cet article de mon blog précédent ici

Quel titre barbare! En fait c’est le message d’erreur remonté lorsque l’on essaye de parser un fichier xml.

C‘est le genre d‘erreur, je trouve, qui peut faire perdre pas mal de temps :

The Processing Instruction Target Matching « [xX][mM][lL] » is Not Allowed

Bizarrement dans mon IDE favori (Eclipse, à l’époque) je l’ai eu en francais :

La cible de l’instruction de traitement correspondant à « [xX][mM][lL] » est interdite

Cette erreur est du au fait qu’il y ai un espace, ou une ligne, avant la ligne entête :

En référence le site ou j’ai trouvé la solution : ici

Principles of Reactive Programming sur Coursera

Aujourd’hui se termine la formation en ligne Principles of reactive programming proposée par Martin Odersky, Erik Meijer, Roland Kuhn.

C’est la seconde édition de ce MOOC, la première édition avait eu lieu en 2013, autant dire que la deuxième session a su se faire attendre :).

Cette formation est assez complète et propose de voir les différents aspects de ce style de programmation.

  • Utilisation des Futures
  • Utilisation des Observables
  • Utilisation des Actors

L’ensemble de la formation se déroule sur 7 semaines. Chaque semaine, une série de vidéos sont publiées et un exercice à réaliser.

La première semaine a été consacrée à une montée en compétence sur l’outil de test de Scala
Les 3 semaines suivantes ont été consacrées aux Future/Observable
Les 2 dernières à l’utilisation du framework d’AKKA, les actors

Cette année, le programme a été un peu chamboulé. En effet, la première semaine des personnes se sont plaintes de la difficulté à finaliser les tests dans les temps. Les organisateurs ont pris la décision d’allonger la période de rendu de 1 à 2 semaines.

Cette modification a eu pour effet réduire le nombre de cours. Les vidéos de la dernière semaine ont quand même été fournies.

Pendant cette formation, nous avons 3 intervenants. Chacun avait son style :

  • Martin Odersky : un style monocorde et très académique. un petit peu ennuyeux
  • Erik Meijer : un style fun et très pragmatique. Très dynamique
  • Roland Kuhn : un style hybride et très pédagogue. Très clair

Les points importants que l’on peut retirer de cette formation:

  • Apprentissage de l’utilisation de Future / Observable. Si l’on part de 0, cela donne un aperçu avancer sur la manière de programmer avec ces composants.
  • Apprentissage de l’utilisation des Actors : Idem, les deux séries d’exercices proposées, permettent de prendre en main le framework et d’en comprendre les concepts.

Les pré-requis pour suivre ce cours ne sont pas nombreux, selon moi :

  • Avoir un vernis sur le langage Scala

Je pense que c’est à peu près tout.

Nous avons été plusieurs collègues de Zenika à suivre ce cours en ligne. Nous avions créé sur Slack un groupe de discussion. Cela nous a permis d’échanger et de s’entraider. Durant la formation, nous n’avons pas échangé de code (afin de respecter le code d’honneur de Coursera), mais des conseils, des pistes à explorer.

On prévoit d’organiser une rétro pour échanger sur la formation et partager les différentes solutions que nous avons rendues.

Lien vers la formation

Devoxx France 2015 – 4 ième édition

Les préparatifs

Mercredi 8 avril 2015 au soir, préparation aux deux jours de conférence. La check list n’a pas beaucoup changé par rapport à l’année dernière :

  • Batterie du téléphone portable chargée à bloc,
  • Batterie du lap top idem,
  • Bloc-notes + stylos,
  • Tenue confortable,
  • Lunette (ça c’est nouveau! :)),
  • Mail permettant de retirer mon badge.

Ensuite vient l’organisation de la journée, établir le programme des conférences auquel je souhaite assister. Et c’est là que tout se complique.
En effet le grand changement, par rapport à 2014, c’est le lieu ou se tiendra la conférence.

L’année dernière, Devoxx France se tenait au Marriott, cette année c’est au palais des Congrès que se déroule l’évènement.

Beaucoup plus de conférences proposées. les choix deviennent difficiles :). En effet là ou l’année dernière 5 conférences se déroulaient en même temps, cette année, il y aura jusqu’à 8 conférences en parallèle.

Après un arbitrage sévère, le programme est enfin prêt (même si je sais que tout est encore mutable. :P)

Il ne me reste plus qu’à me coucher tôt pour être frais et dispo le lendemain du grand jour.

L’arrivée porte Maillot fait son petit effet. Lorsque l’on rentre dans le palais des Congrès que l’on monte au niveau 2, la conférence prend une nouvelle dimension par rapport aux années précédentes, c’est impressionnant.

1800 développeurs passionnés sont réunis, cela représente 300 personnes supplémentaires par rapport à l’année dernière. L’augmentation du nombre de convives ne se ressent pas trop étant donné l’espace disponible.

Les Keynotes

Keynote de l’équipe Devoxx France

Bref, rentrons dans le vif du sujet. La journée commence par une première keynote tenue par Nicolas Martignole, Antonio Goncalves et Zouheir Cadi, les fondateurs de la conférence française.
Durant cette keynote, ils font un bilan sur le chemin parcouru pour aboutir à cette édition 2015.

Ils font également une annonce importante; la Conférence JMaghreb rejoint la famille Devoxx et devient Devoxx Maroc. Comme les autres membres de la famille cette conférence sera un rendez-vous incontournable des développeurs, passionnés et entrepreneurs.

Au programme des sessions, des ateliers pratiques, des tables rondes, hackatons autour des sujets techniques liés au web, mobile, devops, gaming, sécurité.

Devoxx Maroc se tiendra au Mégarama de Casablanca du 16 au 18 novembre 2015.

Pour ceux qui souhaiterait tenter l’aventure de Devoxx Maroc en tant que speaker, le CFP ouvre le 1er mai prochain.

Le futur de la robotique personnelle

La seconde keynote, intitulé robot humanoïde pour tout le monde, a été présentée par Rodolphe Gelin sur le thème du futur de la robotique personnelle.

Il est directeur de la recherche dans la société Aldebaran.

Il commence par présenter différents robots développés par Aldebaran Nao, Pepper, Romeo.

Pour illustrer le thème de sa keynote, il nous propose une vidéo mettant en situation le premier robot de la liste (Nao). On voit une grand-mère interagir avec le robot.
Elle commence par l’appeler et lui demander de venir. Ensuite, elle lui demande les dernières informations importantes de l’actualité. Le robot s’exécute et lui énumère la liste. Lorsque la dame veut avoir plus de détails sur un titre, elle le demande naturellement au robot qui lui donne plus d’informations.

Cette saynète montre comment les robots pourront être utiles dans un futur proche dans le cadre de personne âgées pouvant être isolée socialement.
Au-delà de la diffusion des informations, Nao pourra aussi servir de boîte mail interactive et aussi répondre au téléphone. Sur ce dernier point, Rodolphe Gelin insiste sur le fait, que cette fonctionnalité peut, par exemple, éviter des accidents.

En règle générale, les personnes agées se précipitent sur le téléphone lorsqu’elles reçoivent un appel et que c’est une des causes principales de chute. Avec le robot, cela limitera ces situations puisqu’il pourra prendre l’appel et éviter à la personne de faire un déplacement précipité.

A la fin de cette vidéo (fiction), le speaker précise que la situation montrée n’est pas encore possible. Aujourd’hui, le principal frein à la fluidité dans les interactions entre humain et robot dans une situation courante de la vie de tous les jours est la difficulté pour le robot à entendre et interpréter les messages oraux qui ne lui sont pas adressés explicitement.

Par exemple, vous êtes dans votre canapé, le robot est dans une autre pièce vous lui demandez quelque chose, il aura des difficultés à savoir qu’on s’adresse à lui directement et surtout entendre l’ordre. La raison principale à ce problème est la pollution sonore (ventilateur CPU, lorsqu’il parle, les bruits périphériques, etc).

Rodolphe Gelin se veut rassurant, ces obstacles ne sont évidemment pas insurmontable mais il reste tout de même du travail pour atteindre cette cible.

Les perspectives des travaux menés permettront de faire en sorte que le robot puisse évoluer au contact de la personne qu’il accompagnera. Il apprendra au fur et à mesure du comportement de la personne et à termes à réfléchir par rapport à ses habitudes.
Il pourra également, surveiller des comportements suspects comme une chute, un endormissement trop fréquent, etc) et dans ces situations critiques prévenir autrui.

D’ailleurs les organismes d’assurance s’intéressent particulièrement à ce projet. Il permettra de rationaliser les situations d’urgence.

Un point sur lequel Rodolphe a insisté est que l’on pourrait penser que ce petit robot isolerai socialement les personnes âgées. A cela, il répond que bien au contraire, le robot pourrait également se soucier de cet aspect et créer ce lien social en créant le contact avec d’autres personnes (proposer des appels, proposer des envois de mail, etc).

Ensuite, il nous a montré plusieurs petites vidéos présentant comment le robot pourrait apprendre de son environnement (au niveau des sons, des mouvements, etc)

Enfin, Rodolphe Gelin conclut par le fait que tous les apprentissages réalisés par les robots devront être partagés pour permettre de capitaliser sur ces connaissances précieuses.

Aussi, il a mis en avant les problématiques de sécurité et le fait que les robots devaient absolument être protégé d’une manière ou d’une autre du piratage étant donné la place qu’il prendrait dans le quotidien des humains.
Sur ce dernier sujet, il implique la responsabilité du développeur par rapport au fait qu’un robot est un outil et comme tous outils, il peut servir à faire des choses bien, ou pas…

Bien qu’elle ait une teinture de science-fiction (assez proche) cette keynote était très intéressante et ouvre beaucoup de perspectives sur l’avenir.

La problématique du contrôle des technologies de l’information

La troisième keynote est présentée par Eric Filiol. Directeur du centre de recherche de l’ESIEA, il est un expert français en cryptologie et virologie informatique. Il a été lieutenant-colonel de l’armée de terre française.

On entend de plus en plus de faits mettant en cause les libertés d’expression et les actions visant à imposer un contrôle sur les technologies de l’information.

Eric rappelle, que depuis 60ans les technologies de l’information sont faites par des développeurs … mais que depuis 15 ans, elles sont régies par des personnes n’y comprenant rien, les Hommes politiques.

Si l’on regarde l’histoire passée, la gouvernance était organisée de façon pyramidale ou le sommet envoyait des ordres à la base. Les technologies de l’information (internet) ont changé cette organisation verticale; L’information circule beaucoup plus facilement et peu à peu s’impose un mode collaboratif.

Les strates politiques n’arrivent pas à prendre le train marche et plutôt que de s’adapter et d’accompagner ce mouvement, ils préfèrent en prendre le contrôle et de mettre sous surveillance les agissements des citoyens. Pour un état cette prise de contrôle abusive est un aveu de faiblesse.

Plusieurs faits marquant de tentatives de contrôle comme Cocom, Itar, arrangements de Wassenaar, etc.

Toutes ces actions sont légitimées, par nos politiques, par les faits terroristes et autres tragédies marquantes de l’actualité.

Eric Filiol cite une phrase historique du Cardinal de Richelieu : « Qu’on me donne 6 lignes écrites à la main de n’importe quel honnête homme j’y trouverai de quoi le faire pendre« .

En laissant les choses faire par les groupes politiques, on s’expose à une dictature de la donnée, une perte des valeurs démocratiques fondamentales et une perte du libre arbitre.

Eric Filiol nous donne une nouvelle citation : « Pas de libre arbitre -> pas de liberté … Pas de vie privée -> pas de libre arbitre« .

Par cette phrase, il nous invite à rester vigilant sur l’importance de garder un droit sur sa vie privée et de défendre fermement nos données privées. (même si l’on peut considérer que l’on n’a rien à cacher).

Il note les évolutions positives qu’il faudrait engager pour aller dans ce sens :

  • Le respect de la vie privée,
  • Inscription de ce respect dans la constitution,
  • Renforcer le rôle du juge d’instruction,
  • Créer un comité d’éthique.

Enfin il conclut en remettant au centre le développeur.

Il explique que le développeur à une part de responsabilité dans cette ‘bataille‘. Il est le gardien de la technologie (limiter les bugs, les backdoors, la qualité, etc) dans ce sens une responsabilité morale.

###Reading and Writing in 20 Years

Le dernière keynote est présentée par Dan Allen project leader sur le projet asciidoctor.

Dan est venue parler de l’importance des écrits dans l’histoire de l’humanité.
Toutes les formes d’écritures depuis la nuit des temps ont permit de comprendre comment nos ancêtres vivaient.

Au tout début, les écrits étaient des dessins, puis l’écriture est arrivée, ensuite c’est l’imprimerie et maintenant l’ère du numérique avec les outils informatiques.
Jusqu’à une vingtaine d’années en arrière, les livres étaient la source principale de la connaissance, aujourd’hui ce support physique est en ‘déclin‘.

Ce déclin n’est absolument pas synonyme que l’homme s’est arrêté d’écrire bien au contraire, il n’a jamais été autant productif en matière d’écriture. Ce qui a changé c’est le support d’écriture.

Au-delà du simple faite d’écrire, ce qui permet de conserver cette mémoire à travers les âges c’est le partage de cette information.

Aujourd’hui, si le partage des écrits est facilité par le support numérique, il se pose quand même la question du format de partage. Le standard adopté (en général) est le format PDF, bien que celui-ci a été créé plus pour l’impression. Ce que Dan propose est plutôt la mise en place d’un format pivot permettant l’écriture, l’échange et à terme l’impression. Mais quel pourrait être ce format ? 😉

Un autre problème pouvant interférer est le langage. En effet, un écrit rédigé dans une langue, peut-être traduit dans plusieurs autres languages. Le problème est la précision avec laquelle la traduction est faite. On peut perdre du sens sur l’idée originelle et par conséquent induire des erreurs pour les générations futures.

En conclusion, Dan Allen encourage l’écrivain qui est en nous a continuer d’écrire et partager sans retenue. (l’idéal serait aussi de la faire en Asciidoctor 😛 )

Les conférences

Voici une synthèse des conférences auxquelles j’ai assisté. Le thème global des conférences que j’ai pu suivre s’oriente sur des sujets Big Data :

  • Machine learning et régulation numérique
  • Algorithmes distribués pour le Big Data
  • Machine Learning avec Spark, MLLib et D3.js
  • Un Spotify à la maison avec Spark & Cassandra (Hands on)
  • Stockage et analyse temps réel d’événements avec Riak chez Booking.com

ainsi que des sujets l’agilité et le dev front:

  • Développeur sous influence,
  • Stratégie de mise en place de revues de code (Quickies),
  • L’expérience utilisateur est importante pour nous,
  • React, une autre façon de penser vos composants graphiques

et enfin deux sujets plus orientés tools et dev backend :

  • Les monoïdes démystifiés, en Java et avec des verres de bière
  • Applications Concurrentes Polyglottes avec Vert.x

Ci-dessous différents compte-rendus des conférences que j’ai tout particulièrement apprécié.

Machine learning et régulation numérique

C’est la première présentation à laquelle j’ai assisté. Autant dire que cela commençait très très bien. Aux commandes, deux excellents speakers : Guillaume Laforge et Didier Girard.
Ils sont venues nous parler du machine learning et nous présenter ses concepts ainsi que les rouages de cette discipline.

En introduction, ils ont montré que chaque jour nous subissons le machine learning au travers de nos services favoris :

  • Gmail et la gestion du spam,
  • Amazon et son système de recommandations,
  • Netflix et la gestion de leur catalogue, un système de recommandations également (50% du contenu regardé est issu d’une recommandation),
  • FaceBook sélectionne l’information proposée par un moteur de recommandations.

Donc qu’est-ce que le machine learning?
Le machine learning est la science qui permet à un algorithme d’apprendre sans avoir été explicitement programmé pour cela. Cette science se base sur un ensemble d’algorithmes permettant de classifier, regrouper, détecter des comportements communs entre des données.

Ils ont fait une présentation synthétique des différents types d’algorithmes mis en jeu :

  • Algo Supervisé : Regression, Classification
  • Algo Non supervisé : Clusterisation, séparation des sources

La présentation s’est articulée autour de plusieurs commandements qui font le coeur de cette matière.

1er commandement – Tout Données tu collecteras

Il important d’avoir un jeu de données très important pour la création d’un modèle fiable qui puisse être généralisé.

2ième commandement – Les données tu analyseras

Une fois collectées, les données doivent être analysées pour leur donner un sens. Durant cette phase il ne faut pas tomber dans le piège de la corrélation et de la causalité. Comme par exemple le lien entre la consommation de chocolat et le faite d’obtenir un prix Nobel.

3ième commandement – N’apprend pas ce que tu sais déjà

Un modèle va apprendre au plus facile. Plutôt que de dire à mon modèle apprend les choses évidentes. On va supprimer les données que l’on connait déjà, ainsi le modele apprendra ce que je n’arrive pas à modéliser.

4ième commandement – Tes données tu pré traiteras

Il est important de pré traiter les données afin de faciliter l’analyse. Par exemple, pour faire de la reconnaissance faciale. On va appliquer à la photo un traitement avant de lancer la reconnaissance (enlever la couleur, faire crop, redimensionner, augmentation des contrastes)

5ième commandement – Ton algorithme tu choisiras

Pour faire l’analyse des données, il faudra choisir l’algorithme le plus adapté à la situation :

  • les plus proches voisins
  • Support vector machine
  • Random forest – Decision tree
  • Neutral network
  • Back propagation
  • Deep learning

6ième commandement – De l’intuition tu auras

En visualisant les données,le data scientiste, sur la base de son expérience, va être capable de percevoir des tendances.

7ième commandement : Ton système tu entraineras

Lorsque l’on créé un modèle il faut être capable de l’entrainer. Sur l’ensemble du jeu de données à disposition il faut faire une répartition : 60% pour l’apprentissage et 40% pour faire la généralisation (c’est proportion peuvent varier en fonction du contexte).

8ième commandement : Par coeur tu n’apprendras pas

Il faut faire attention au sur-apprentissage. Si le système apprend trop longtemps, il risque de finir par apprendre par coeur et les résultats seront faussés lors de la généralisation.

Didier et Guillaume nous ont énuméré une liste d’outils utilisés pour le machine learning :

  • R,
  • Weka
  • Octave
  • Google Prediction
  • Apache Mahout
  • Prediction.io
  • Mad.io
  • Scikit Learn

Avec cette présentation, le tour d’horizon est complet et peut servir de base pour commencer à aborder la science du machine learning.

Ils nous rappeler au terme de cette présentation le slogan suivant : BigData is scoring you.
Donc il faut rester vigilant face aux informations que l’on nous suggère. Elles ont tendance à nous cloisonner dans l’information que l’on perçoit.

Développeur sous influence

Cette conférence était proposée par Guillaume Duquesnay. Guillaume est un coach agile @ dévelopeur vétéran.

Fort de son rôle de coach agile, Guillaume a fait un état de lieu des problèmes que l’on peut rencontrer au sein d’une équipe. La frustration qu’un développeur peut ressortir parce que les chefs ne sont pas réceptifs à la nécessité d’investir sur tel ou tel sujet ou lorsque le vétéran de l’équipe impose une dictature technique au sein de l’équipe sans prendre en compte les idées de chacun.

Ces situations amènent des points de blocage ou le développeur sent qu’il est cantonné à son périmètre et ne peut en aucun cas influencer son entourage.

Être influent, c’est quoi? : l’art de changer les choses au quotidien, faire changer la trajectoire
Pour cela, il faut :

  • Avoir quelque chose à dire,
  • Savoir confronter son avis avec les autres,
  • Savoir à qui s’adresser, ou quand l’ouvrir,
  • Savoir ce que l’on fait là.

Savoir être influent va de pair avec l’expérience acquise.

Il nous décrit ensuite les différents points en y appliquant des bonnes pratiques.

Ai je quelque chose à dire ?

Il faut parler dans l’intérêt du projet. Maitriser les impacts sur le projet des propositions que l’on est en train de faire.

Se confronter aux autres avis?

Il est important de toujours dire ce que l’on pense, et ne surtout pas intérioriser. Cette transparence impose de respecter des règles de diplomatie :

  • Parler du code et uniquement du code,
  • Ne pas parler des gens,
  • Pas de reproches,
  • Plus on s’approche des coupables plus on s’éloigne de de la solution.

Une règle importante à retenir : est que si l’on attaque quelqu’un, il va se défendre.

Quand tenir sa position?

Une nouvelle lié à l’intégrité, ne pas changer d’avis sous l’influence d’un manager. Egalement, s’il n’y a pas de nouvel élément rationnel dans la discussion alors il n’y a pas de raison de changer d’avis.

Savoir et apprendre à avoir tord

Reconnaitre son erreur, c’est accepter d’apprendre de nouvelles choses. C’est être également capable de sortir de sa zone de confort. Savoir avoir tord c’est également oublier la pression sociale, avoir tord c’est être faible.

Guillaume nous donne quelques références pour améliorer nos échanges.

  • Chercher à comprendre avant d’être compris
  • Observer l’autre pour s’observer soi
  • La conscience de soi

Parler aux chefs?

Pour s’adresser à l’instance décisionnaire, il faut savoir identifier qui pourrait être un sponsor de poids à même de défendre votre projet.
Pour accrocher la personne, commencer par la conclusion, en proposant de développer s’il elle le souhaite.
Trouver les endroits stratégiques pour rencontrer la personne cible par pure coïncidence (pause cigarette, pause café, etc).

Pas de démarche en sous marin

Il ne faut surtout pas mettre les gens au pied du mur, en ayant fait tout seul dans son coin. Les personnes se sentiraient pas impliquées et vous n’auriez pas leurs soutiens pour la suite.

Que fais je là ? / quelle est ma position?

Il est important dans cette démarche de savoir ce que l’on fait là. Guillaume fait état de deux raisons :

  • Dépendance à la situation (Financiere, affective)

    vs

  • Recherche de performance

Il faut savoir trancher.

Je veux tout changer!

Il ne faut pas se disperser et choisir ses batailles. Tout mener de front ne sert à rien. Il faut également savoir apprécier les succès qu’ils soient grands ou petits.

En conclusion, devenir influent est un travail de longue haleine. c’est quasiment un choix de carrière qu’il faut bâtir patiemment.

###L’expérience utilisateur est importante pour nous

Cette conférence a été présentée par Florence Herrou. L’objectif de cette présentation est de nous faire découvrir l’expérience utilisateur lors de la conception d’applications métier.

Pour commencer Florence par nous rappeler l’UX n’est pas liée uniquement à l’ergonomie. Ce domaine est plus vaste et touche directement le besoin métier. La mise en place d’une UX est un processus itératif (prototype présenté aux utilisateurs, récolte des impressions, boucle).

Elle cite d’ailleurs Steeve Jobs en donnant sa définition : l’UX c’est répondre au besoin

Pour illustrer ses propos, Florence nous montre un exemple ou deux professeurs (issus du monde star wars) tentent d’utiliser un outil ou le design a été bien pensé d’un côté et mal de l’autre. Elle met en évidence les conséquences d’un mauvais design et les difficultés que cela engendre dans l’expérience utilisateur.

Ensuite, elle présente l’approche cycle en V d’un projet. Elle explique que cette méthode conduit généralement le projet à l’échec. L’effet tunnel entre l’analyse du besoin et la livraison empêche tout évolution du besoin potentiel ou pire ne permet pas de détecter une incompréhension de la demande.

L’approche agile permet d’identifier rapidement s’il y a une divergence de compréhension entre le besoin exprimé et ce qui a été réalisé.

Ce qu’il est important de garder à l’esprit, c’est que l’utilisateur à toujours raison, il est le seul à pouvoir valider si l’UX répond au besoin ou non.

Par conséquent il faut absolument le garder au centre des débats.

Pour savoir si le design que l’on est en train de mettre en place est correcte ou non, il faut en permanence le tester. Pour cela il existe plusieurs techniques :

  • En commençant par du papier/crayon avec les utilisateurs
  • Soumettre au user/PO où quelqu’un pour voir si déjà c’est bien compris et clair.
  • Faire de l’AB testing
  • Après lancement, faire des questionnaires simples et rapides aux utilisateurs.

Florence nous des conseils sur comment susciter l’interêt (conscient ou inconscient) chez l’utilisateur avec un bon design.

Obtenir un comportement désiré :
* En proposant un workflow évident de décision dans l’application
* Agir sur des déclencheurs internes ou externes de l’utilisateur
* Gamifier l’application (créer de l’engagement, faciliter la progression, créer de nouvelle habitude, quête de niveaux et de gratifications), par contre un excès peut entrainer une décrédibilisation.
* Le storytelling : présenter l’application comme une histoire.

En conclusion, Florence nous rappelle l’importance de ne pas négliger l’UX et d’avoir à coeur de bien adresser le besoin exprimé par les utilisateurs. Sans quoi, l’application aura vocation à rester au stade de projet.

Machine Learning avec Spark, MLLib et D3.js

Cette conférence a été présenté par Hayssam Saleh. Les slides de la présentation sont disponibles ici

En introduction, Hayssam nous explique au travers d’un exemple concret les motivations à mettre en oeuvre du machine learning. Il commence par nous exposer la liste des cas d’utilisation classiques :

  • Faire de la recommandation de produit,
  • Classification de contenu en groupe prédéfinis,
  • Regroupement de contenu similaire,
  • Recherche d’association/pattern dans les actions et le comportement,
  • détection de fraude et d’anomalie,
  • Identification de topic clefs (politique).

Pour accompagner son propos, il nous propose de dérouler un exemple concret. Le cas d’utilisation qu’il a pris est le suivant :

Estimer si je peux prêter de l’argent à quelqu’un dans le cadre d’un prêt bancaire

Ci-dessous les paramètres du problème :

  • Entrée : Prédicteurs /vars indépendantes (salaire, statut marital, propriétaire…)
  • Sortie : Réponse / label (variable dépendante).

Si l’on devait répondre à cette question pour une personne, la démarche serait simple. Mais dans notre cas, il faut traiter un volume de personne très important.

Les solutions envisageables :

  • 1 ) embaucher beaucoup de gens pour faire l’analyse (chère, beaucoup d’attente)
  • 2 ) Mise en place d’un moteur de règles Jboss rules (les règles se multiplient et sont difficiles à maintenir, évolution des clients, maintenance)
  • 3 ) Machine Learning (précis, autonome, scalable)

Pour mettre en place cette solution, l’outil retenu est Spark associé à la librairie MLib. Pourquoi cet outil?

Dans le monde du BigData, deux univers se côtoient : celui du Data Scientist et celui du développeur.

Dans le cas du data Scientist on parlera d’analyse d’investigation et pour le développeur d’analyse opérationnelle. Selon les cas, les besoins ne sont pas les mêmes :

Analyse d’investigation Analyse opérationnelle
Echantillon de données Données de production
Poste de travail Cluster serveur
Requête Ad hoc offline Sollicitation online continue
Métrique : la précision Métrique : le temps de réponse
Facilité de développement Performance

Spark permet de rapprocher ces deux mondes par la simplicité de sa mise en oeuvre.

Ensuite, il est nécessaire de préparer les échantillons de données.

  1. Phase cruciale, elle doit être faite de façon très rigoureuse,
  2. Prendre un échantillon des données des scoring qui ont déjà été réalisés,
  3. Diviser en 2 lots d’échantillons,
    • Un lot pour le développeur pour construire un modele et tester son modele,
    • L’autre à destination du statisticien pour vérifier la validité du modèle créé

Le modele est considéré satisfaisant lorsque le niveau de précision est satisfaisant.

Si la performance du modèle n’est pas bonne, il y a trois causes possibles :

  • Les prédicteurs sont mal choisis (biaisé) -> Changer les prédicteurs
  • L’échantillon n’est pas représentatif ou l’algo de ML n’est pas représentatif (overfitting)
    • Solution : réduire les prédicteurs car il n’a aucun rôle et perturbe l’algorithme,
  • L’algo de ML est inadapté (underfitting) -> passer par une étape de visualisation
  • Aussi, le cumul des trois.

Pour détecter qu’un algorithme est inadapté il faut passer par une étape de visualisation.

Hayssam a poursuivi sa présentation en nous donnant des informations sur les algorithmes disponibles dans MLLib.

  • Classification & Regression
    • Prédire le label de nouvelles données à partir des labels des données existantes.
  • Clustering : apprentissage non supervisé
    • Prédire le label de donnée existant à partir de leurs prédicteurs
  • Collaboratif filtering
    • Prédire l’intérêt d’un utilisateur pour un item (eg Amazon).
  • Frequent pattern matching
    • Extraire les produits les plus souvents achetés ensemble dans le même panier.
  • decision Tree (arbre de décision)
    • Recherche de la meilleure condition de segmentation en s’appuyant sur l’impureté.

(* Impureté : Mesure la qualité de la séparation.*)

Ensuite, Hayssam à travers des screenshoots de code, nous montre la simplicité de mettre en oeuvre un algorithme (ici le decision Tree) dans Spark MLLib. Voici les différentes étapes présentées :

  1. Chargement des données à partir d’un fichier csv
  2. Recherche de l’arbre
  3. Evaluation de la performance

Chaque étape est présentée sous forme d’un slide et à chaque on trouve très peu de ligne de code pour là réaliser.

La dernière partie du talk est consacrée à la visualisation.
La visualisation est une étape importante dans l’élaboration d’un modèle. Pour avoir la représentation graphique des résultats Hayssam utilise une librairie javascript basée sur D3.js : statapps.
Cette librairie propose plusieurs types de graphique permettant de comprendre et analyser les données.

En conclusion, le triptyque Spark, MLLib, D3.js sont une base pertinente et efficace pour faire du machine learning.
Spark/MLLib permet de briser les murs qui sépare le DataScientist et le développeur (une sorte de Devops).

###React, une autre façon de penser vos composants graphiques (Tools in action) **

Ce Tools in Action est proposé par Mathieu ANCELIN. Les slides sont disponibles ici

Il nous présente de cette session la librairie développée par FaceBook : React. L’objectif de React est de rendre des vues et répondre à des événements.
La librairie a été mis en Open Source depuis 2013. Les cibles adressées par React sont de grosses applications javascript avec des données qui changent tout le temps.

Le framework se base sur une approche déclarative et orientée composant autonome, composable, réutilisable.

Les principaux utilisateurs de React (en prod)

  • instagram,
  • FB,
  • FlipBoard,
  • Yahoo,
  • Netflix (Actuellement, c’est une page web surtout les devices. d’ici à la fin de l’année un passage en full React est prévu.)

Les concepts

  • Virtual DOM : Avant de rafraichir la vue, il prend un snapshot de la vue courante, calcule la nouvelle vue et fait un diff entre les deux. Ensuite il va ‘patcher‘ le DOM avec les choses qui ont changé.

  • Synthétic Event : React a un système d’évènement au-dessus du DOM conforme aux recommandations W3C. Cette surcouche a pour objectif de standardiser la gestion des événements de manière transverse à tous les navigateurs.

Les fonctionnalités

  • Source de données : Props & state

    • Props = propriéré d’entrée du composant
    • State : le composant doit être immutable, s’il y a un changement d’état, alors lancement d’une MAJ de la vue.
  • Cycle de vie du composant. Le framework fournit des points d’entrées sur le cycle de vie des composants
    • componentdidMount,
    • componentdWillMount,
    • componentdWillUpdate
    • componentdWillUpdate
  • Validation des props du composant
    • En typant les props, React est capable de valider les entrées fournies au composant.
  • Gestion des erreurs propre
    • Les messages d’erreurs sont très clair.
  • Mixins
    • permet d’ajouter du comportement au composant.
    • plusieurs mixins peuvent être ajoutés à un composant
    • ils seront appelés dans l’ordre de la déclaration.
  • Test
    • React propose TestUtils pour les tests

En conclusion, cet outil semble très complet et fournit de très bonnes performances comparativement à ses concurrents :
* Ember 7 frames par second
* Angulae : 6-7 frames par second
* React : 14 frame / s (par defaut)
* Reactive.js 17 frame/s

La dernière version de React support ES6 et supporte également le rendu de WebComponent. L’écosystème est très large (intégration bootstrap, matériel design, highCharts,etc) et peut-être également utilisé pour du rendu natif sur device mobile.

Conclusion

Ces deux jours de conférence ont été très riches et très denses. Les conférences étaient de qualité. La diversité des sujets proposés permettait de couvrir un spectre assez large de connaissance.
Le deuxième jour, j’ai pu assister à un Hand’s on sur Spark et Cassandra, ce qui m’a permis de mettre les mains dans le code.

D’une manière générale, l’organisation de l’événement a été parfaitement menée. La grande nouveauté de cette année, le lieu de la conférence a contribué à la réussite de Devoxx France.

Créez un site Web ou un blog gratuitement sur WordPress.com.

Retour en haut ↑