Tutoriel pour apprendre le langage Scala par l'exemple


précédentsommairesuivant

3. Programmer avec des acteurs et des messages

Voici un exemple montrant un domaine d'application pour lequel Scala est particulièrement bien adapté. Supposons que l'on veuille mettre en place un service d'enchères électroniques. Nous utiliserons le modèle de traitement par acteurs d'Erlang pour implémenter les participants aux enchères. Les acteurs sont des objets auxquels on envoie des messages. Chaque acteur dispose d'une « boîte aux lettres » dans laquelle arrivent ses messages et qui est représentée comme une file d'attente. Il peut traiter séquentiellement les messages de sa boîte ou rechercher les messages qui correspondent à certains critères.

Pour chaque article mis aux enchères, il y a un acteur commissaire-priseur qui publie les informations sur cet article, qui reçoit les offres des clients et qui communique avec le vendeur et celui qui a remporté l'enchère afin de clore la transaction. Nous présenterons ici une implémentation simple de cet acteur.

La première étape consiste à définir les messages échangés au cours d'une enchère. Nous utiliserons deux classes de base abstraites, AuctionMessage pour représenter les messages des clients adressés au service des enchères et AuctionReply pour représenter les réponses du service aux clients. Pour ces deux classes de base, il existe un certain nombre de cas, définis dans le code ci-dessous.

Classes des messages
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
import scala.actors.Actor

abstract class AuctionMessage
case class Offer(bid: Int, client: Actor)       extends AuctionMessage
case class Inquire(client: Actor)               extends AuctionMessage

abstract class AuctionReply
case class Status(asked: Int, expire: Date)     extends AuctionReply
case object BestOffer                           extends AuctionReply
case class BeatenOffer(maxBid: Int)             extends AuctionReply
case class AuctionConcluded(seller: Actor, client: Actor)
                                                extends AuctionReply

case object AuctionFailed                       extends AuctionReply
case object AuctionOver                         extends AuctionReply

Ces case classes définissent le format des différents messages de chacune des classes de base. Ces messages pourraient être traduits en petits documents XML, car nous supposons qu'il existe des outils automatiques permettant d'effectuer des conversions entre documents XML et ces représentations internes.

Le code ci-dessous présente une implémentation Scala d'une classe Auction permettant de représenter les acteurs qui coordonnent les enchères pour un article particulier. Les objets de cette classe sont créés en indiquant :

  • un acteur vendeur qui doit être prévenu lorsque l'enchère est terminée ;
  • une enchère minimale ;
  • la date de fin des enchères.
Implémentation de la classe Auction
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
class Auction(seller: Actor, minBid: Int, closing: Date) extends Actor {
    val timeToShutdown = 36000000 // msec
    val bidIncrement = 10
    def act() {
        var maxBid = minBid - bidIncrement
        var maxBidder: Actor = null
        var running = true
        while (running) {
            receiveWithin ((closing.getTime() - new Date().getTime())) {
                case Offer(bid, client) =>
                    if (bid >= maxBid + bidIncrement) {
                        if (maxBid >= minBid) maxBidder ! BeatenOffer(bid)
                        maxBid = bid; maxBidder = client; client ! BestOffer
                    } else {
                        client ! BeatenOffer(maxBid)
                    }
                case Inquire(client) =>
                    client ! Status(maxBid, closing)
                case TIMEOUT =>
                    if (maxBid >= minBid) {
                        val reply = AuctionConcluded(seller, maxBidder)
                        maxBidder ! reply; seller ! reply
                    } else {
                        seller ! AuctionFailed
                    }
                    receiveWithin(timeToShutdown) {
                        case Offer(_, client) => client ! AuctionOver
                        case TIMEOUT => running = false
                    }
            }
        }
    }
}

Le comportement de l'acteur est défini par sa méthode act qui passe son temps à sélectionner un message (avec receiveWithin) et qui y réagit ; ceci jusqu'à la fin de l'enchère, qui est signalée par le message TIMEOUT. Avant de se terminer, l'acteur reste actif pendant une période déterminée par la constante timeToShutdown et répond aux autres offres que l'enchère est terminée.

Voici quelques informations supplémentaires sur les constructions utilisées dans ce programme :

  • • la méthode receiveWithin de la classe Actor prend en paramètres une durée en millisecondes et une fonction qui traite les messages de la boîte aux lettres. Cette fonction est décrite par une suite de cas qui précisent, chacun, un motif et une action à réaliser lorsqu'un message correspond à ce motif. La méthode receiveWithin sélectionne le premier message de la boîte aux lettres qui correspond à l'un de ces motifs et lui applique l'action correspondante ;
  • • le dernier cas de receiveWithin utilise le motif TIMEOUT. Si aucun autre message n'est reçu dans l'intervalle, ce motif est déclenché après l'expiration du délai passé à la méthode receiveWithin englobante. TIMEOUT est un message spécial qui est déclenché par l'implémentation de la classe Actor ;
  • • les messages de réponse sont envoyés en utilisant la syntaxe destination ! UnMessage. Le symbole ! est utilisé ici comme un opérateur binaire portant sur un acteur et un message : c'est l'équivalent Scala de l'appel destination.!(UnMessage), c'est-à-dire de l'appel de la méthode ! de l'acteur destination avec le message en paramètre.

Cette présentation donne un avant-goût de la programmation distribuée en Scala. Il pourrait sembler que Scala dispose d'un grand nombre de constructions gérant les processus acteurs, l'envoi et la réception des messages, la gestion des délais d'expiration, etc., mais il n'en est rien : toutes ces constructions sont des méthodes de la classe Actor de la bibliothèque de Scala. Cette classe est elle-même implémentée en Scala et repose sur le modèle sous-jacent des threads du langage hôte (Java ou .NET). La section 17.11Acteurs présentera l'implémentation de toutes les fonctionnalités de la classe Actor utilisées ici.

Se reposer sur une bibliothèque permet d'avoir un langage relativement simple et d'offrir beaucoup de souplesse aux concepteurs des bibliothèques. En effet, le langage n'ayant pas besoin de spécifier les détails de la communication entre les processus, il peut rester plus simple et plus général. En outre, le modèle particulier des messages stockés dans une boîte aux lettres étant un module de la bibliothèque, il peut être librement modifié si d'autres applications ont besoin d'autres modèles. Cette approche nécessite toutefois que le langage soit suffisamment expressif pour fournir les abstractions nécessaires de façon simple. Scala a été conçu dans cet esprit : l'un des principaux objectifs était que le langage soit suffisamment souple pour servir de langage hôte pour des langages spécifiques au domaine implémentés au moyen de modules de bibliothèque. Les constructions permettant la communication des acteurs, par exemple, peuvent être considérées comme l'un de ces langages spécifiques, qui étend conceptuellement le langage Scala de base.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2017 Martin Odersky. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.