 |
 |
8) Interfaces et classes internes |
|
 |
|
Texte original |
 |
Traducteur : Jérome QUELIN |
|
 |
///
|
Ce chapitre contient 6 pages
1
2
3
4
5
6
|
|
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
//: c08:MultiImplementation.java // With concrete or abstract classes, inner // classes are the only way to produce the effect // of "multiple implementation inheritance."
class C {} abstract class D {}
class Z extends C { D makeD() { return new D() {}; } }
public class MultiImplementation { static void takesC(C c) {} static void takesD(D d) {} public static void main(String[] args) { Z z = new Z(); takesC(z); takesD(z.makeD()); } } ///:~
|
 |
//: c08:MultiImplementation.java // Avec des classes concrètes ou abstract, les classes // internes constituent le seul moyen de mettre en oeuvre // « l'héritage multiple d'implémentations ».
class C {} abstract class D {}
class Z extends C { D makeD() { return new D() {}; } }
public class MultiImplementation { static void takesC(C c) {} static void takesD(D d) {} public static void main(String[] args) { Z z = new Z(); takesC(z); takesD(z.makeD()); } } ///:~
|
 |
 |
 |
If you didn’t need to solve the
“multiple implementation inheritance” problem, you could conceivably
code around everything else without the need for inner classes. But with inner
classes you have these additional features:
|
 |
Si le problème de « l'héritage multiple d'implémentations » ne se
pose pas, on peut tout à fait se passer des classes internes. Mais les classes internes fournissent
toutefois des fonctionnalités intéressantes :
|
 |
 |
 |
- The inner class can have
multiple instances, each with its own state information that is independent of
the information in the outer class
object.
- In a single
outer class you can have several inner classes, each of which implement the same
interface or inherit from the same class in a different way. An example
of this will be shown
shortly.
- The point
of creation of the inner class object is not tied to the creation of the outer
class object.
- There
is no potentially confusing “is-a” relationship with the inner
class; it’s a separate
entity.
|
 |
- Les classes internes peuvent avoir plusieurs instances, chacune avec ses
propres informations indépendantes des informations de l'objet de la classe externe.
- Dans une classe externe on peut avoir plusieurs classes internes, chacune
implémentant la même interface ou dérivant la même classe d'une façon différente.
Nous allons en voir un exemple bientôt.
- Le point de création d'un objet de la classe interne n'est pas lié à la
création de l'objet de la classe externe.
- Aucune confusion concernant la relation « est-un » n'est possible
avec la classe interne ; c'est une entité séparée.
|
 |
 |
 |
As an example, if
Sequence.java did not use inner classes, you’d have to say “a
Sequence is a Selector,” and you’d only be able to
have one Selector in existence for a particular Sequence. Also,
you can have a second method, getRSelector( ), that produces a
Selector that moves backward through the sequence. This kind of
flexibility is only available with inner classes.
|
 |
Par exemple, si Sequence.java n'utilisait pas de classes
internes, il aurait fallu dire « une Sequence est un
Selector », et on n'aurait pu avoir qu'un seul Selector pour
une Sequence particulière. De plus, on peut avoir une autre méthode,
getRSelector(), qui produise un Selector parcourant la
Sequence dans l'ordre inverse. Cette flexibilité n'est possible qu'avec les
classes internes.
|
 |
 |
 |
Closures & Callbacks
|
 |
Fermetures & callbacks
|
 |
 |
 |
A closure is a callable object
that retains information from the scope in which it was created. From this
definition, you can see that an inner class is an object-oriented closure,
because it doesn’t just contain each piece of information from the outer
class object (“the scope in which it was created”), but it
automatically holds a reference back to the whole outer class object, where it
has permission to manipulate all the members, even private
ones.
|
 |
Une fermeture est un objet qui retient des informations de la
portée dans laquelle il a été créé. A partir de cette définition, il est clair qu'une classe
interne est une fermeture orientée objet, parce qu'elle ne contient pas seulement chaque élément
d'information de l'objet de la classe externe (« la portée dans laquelle il a été
créé »), mais elle contient aussi automatiquement une référence sur l'objet de la classe
externe, avec la permission d'en manipuler tous les membres, y compris les
private.
|
 |
 |
 |
One
of the most compelling arguments made to include some kind of
pointer mechanism in Java was to allow callbacks.
With a callback, some other object is given a piece of information that allows
it to call back into the originating object at some later point. This is a very
powerful concept, as you will see in Chapters 13 and 16. If a callback is
implemented using a pointer, however, you must rely on the programmer to behave
and not misuse the pointer. As you’ve seen by now, Java tends to be more
careful than that, so pointers were not included in the
language.
|
 |
L'un des arguments les plus percutants mis en avant pour inclure certains mécanismes
de pointeur dans Java était de permettre les callbacks. Avec un callback, on donne
des informations à un objet lui permettant de revenir plus tard dans l'objet originel. Ceci est un
concept particulièrement puissant, comme nous le verrons dans les chapitres 13 et 16. Cependant, si
les callbacks étaient implémentés avec des pointeurs, le programmeur serait responsable de la
gestion de ce pointeur et devrait faire attention afin de ne pas l'utiliser de manière incontrôlée.
Mais comme on l'a déjà vu, Java n'aime pas ce genre de solutions reposant sur le programmeur, et
les pointeurs ne furent pas inclus dans le langage.
|
 |
 |
 |
The closure provided by the inner class
is a perfect solution; more flexible and far safer than a pointer. Here’s
a simple example:
|
 |
Les classes internes fournissent une solution parfaite pour les fermetures,
bien plus flexible et de loin plus sûre qu'un pointeur. Voici un exemple simple :
|
 |
 |
 |
//: c08:Callbacks.java // Using inner classes for callbacks
interface Incrementable { void increment(); }
// Very simple to just implement the interface: class Callee1 implements Incrementable { private int i = 0; public void increment() { i++; System.out.println(i); } }
class MyIncrement { public void increment() { System.out.println("Other operation"); } public static void f(MyIncrement mi) { mi.increment(); } }
// If your class must implement increment() in // some other way, you must use an inner class: class Callee2 extends MyIncrement { private int i = 0; private void incr() { i++; System.out.println(i); } private class Closure implements Incrementable { public void increment() { incr(); } } Incrementable getCallbackReference() { return new Closure(); } }
class Caller { private Incrementable callbackReference; Caller(Incrementable cbh) { callbackReference = cbh; } void go() { callbackReference.increment(); } }
public class Callbacks { public static void main(String[] args) { Callee1 c1 = new Callee1(); Callee2 c2 = new Callee2(); MyIncrement.f(c2); Caller caller1 = new Caller(c1); Caller caller2 = new Caller(c2.getCallbackReference()); caller1.go(); caller1.go(); caller2.go(); caller2.go(); } } ///:~
|
 |
//: c08:Callbacks.java // Utilisation des classes internes pour les callbacks
interface Incrementable { void increment(); }
// Il est très facile d'implémenter juste l'interface : class Callee1 implements Incrementable { private int i = 0; public void increment() { i++; System.out.println(i); } }
class MyIncrement { public void increment() { System.out.println("Other operation"); } public static void f(MyIncrement mi) { mi.increment(); } }
// Si la classe doit aussi implémenter increment() d'une // autre façon, il faut utiliser une classe interne : class Callee2 extends MyIncrement { private int i = 0; private void incr() { i++; System.out.println(i); } private class Closure implements Incrementable {
public void increment() { incr(); } } Incrementable getCallbackReference() { return new Closure(); } }
class Caller { private Incrementable callbackReference; Caller(Incrementable cbh) { callbackReference = cbh; } void go() { callbackReference.increment(); } }
public class Callbacks { public static void main(String[] args) { Callee1 c1 = new Callee1(); Callee2 c2 = new Callee2(); MyIncrement.f(c2); Caller caller1 = new Caller(c1); Caller caller2 = new Caller(c2.getCallbackReference()); caller1.go(); caller1.go(); caller2.go(); caller2.go(); } } ///:~
|
 |
 |
 |
This example also provides a further
distinction between implementing an interface in an outer class vs. doing so in
an inner class. Callee1 is clearly the simpler solution in terms of the
code. Callee2 inherits from MyIncrement which already has a
different increment( ) method which does something unrelated to that
which is expected by the Incrementable interface. When MyIncrement
is inherited into Callee2, increment( ) can’t be
overridden for use by Incrementable, so you’re forced to provide a
separate implementation using an inner class. Also note that when you create an
inner class you do not add to or modify the interface of the outer
class.
|
 |
Cet exemple est un exemple supplémentaire montrant les différences entre
l'implémentation d'une interface dans une classe externe ou une classe interne.
Callee1 est sans conteste la solution la plus simple en terme de code.
Callee2 hérite de MyIncrement qui dispose déjà d'une méthode
increment() faisant quelque chose de complètement différent que ce qui est attendu
par l'interface Incrementable. Quand MyIncrement est dérivée dans
Callee2, increment() ne peut être redéfinie pour être utilisée
par Incrementable, on est donc forcé d'utiliser une implémentation séparée avec
une classe interne. Notez également que lorsqu'on crée une classe interne, on n'étend pas ni ne
modifie l'interface de la classe externe.
|
 |
 |
 |
Notice that everything except
getCallbackReference( ) in Callee2 is private. To
allow any connection to the outside world, the interface
Incrementable is essential. Here you can see how interfaces allow for
a complete separation of interface from implementation.
|
 |
Remarquez bien que tout dans Callee2 à l'exception de
getCallbackReference() est private. L'interface
Incrementable est essentielle pour permettre toute interaction avec le monde
extérieur. Les interfaces permettent donc une séparation complète entre
l'interface et l'implémentation.
|
 |
 |
 |
The inner class Closure simply
implements Incrementable to provide a hook back into
Callee2—but a safe hook. Whoever gets the Incrementable
reference can, of course, only call increment( ) and has no other
abilities (unlike a pointer, which would allow you to run
wild).
|
 |
La classe interne Closure implémente Incrementable uniquement
pour fournir un point de retour dans Callee2 - mais un point de retour sûr.
Quiconque récupère la référence sur Incrementable ne peut appeler
qu'increment() (contrairement à un pointeur, qui aurait permis de faire tout ce
qu'on veut).
|
 |
 |
 |
Caller takes an
Incrementable reference in its constructor (although the capturing of the
callback reference could happen at any time) and then, sometime latter, uses the
reference to “call back” into the Callee
class.
|
 |
Caller prend une référence Incrementable
dans son constructeur (bien qu'on puisse fournir cette référence - ce callback - n'importe quand),
et s'en sert par la suite, parfois bien plus tard, pour « revenir » dans la classe
Callee.
|
 |
 |
 |
The value of the callback is in its
flexibility—you can dynamically decide what functions will be called at
run-time. The benefit of this will become more evident in Chapter 13, where
callbacks are used everywhere to implement graphical user interface (GUI)
functionality.
|
 |
La valeur des callbacks réside dans leur flexibilité - on peut décider dynamiquement quelles
fonctions vont être appelées lors de l'exécution. Les avantages des callbacks apparaîtront dans le
chapitre 13, où ils sont utilisés immodérément pour implémenter les interfaces graphiques
utilisateurs (GUI).
|
 |
 |
 |
Inner classes & control frameworks
|
 |
Classes internes & structures de contrôle
|
 |
 |
 |
A more concrete example of the use of
inner classes can be found in something that I will refer to here as a
control
framework.
|
 |
Un exemple plus concret d'utilisation des classes internes est ce que
j'appelle les structures de contrôle.
|
 |
 |
 |
An application
framework is a class or a set of classes that’s designed to solve a
particular type of problem. To apply an application framework, you inherit from
one or more classes and override some of the methods. The code you write in the
overridden methods customizes the general solution provided by that application
framework, in order to solve your specific problem. The control framework is a
particular type of application framework dominated by the need to respond
to events; a system that primarily responds to events is called an
event-driven system. One of the most
important problems in application programming is the
graphical
user interface (GUI), which is almost entirely event-driven. As you will see in
Chapter 13, the Java Swing library is a control framework that elegantly solves
the GUI problem and that heavily uses inner classes.
|
 |
Une structure d'application est une classe ou un ensemble de
classes conçues pour résoudre un type particulier de problème. Pour utiliser une structure
d'application, il suffit de dériver une ou plusieurs de ces classes et de redéfinir certaines des
méthodes. Le code écrit dans les méthodes redéfinies particularise la solution générale fournie par
la structure d'application, afin de résoudre le problème considéré. Les structures de contrôle sont
un type particulier des structures d'application dominées par la nécessité de répondre à des
événements ; un système qui répond à des événements est appelé un système à programmation événementielle. L'un des problèmes les plus ardus
en programmation est l'interface graphique utilisateur (GUI), qui est quasiment entièrement
événementielle. Comme nous le verrons dans le Chapitre 13, la bibliothèque Java Swing est une
structure de contrôle qui résoud élégamment le problème des interfaces utilisateurs en utilisant
extensivement les classes internes.
|
 |
 |
 |
To see how inner classes allow the simple
creation and use of control frameworks, consider a control framework whose job
is to execute events whenever those events are “ready.” Although
“ready” could mean anything, in this case the default will be based
on clock time. What follows is a control framework that contains no specific
information about what it’s controlling. First, here is the interface that
describes any control event. It’s an abstract class instead of an
actual interface because the default behavior is to perform the control
based on time, so some of the implementation can be included
here:
|
 |
Pour voir comment les classes internes permettent une mise en oeuvre aisée
des structures de contrôle, considérons le cas d'une structure de contrôle dont le rôle consiste à
exécuter des événements dès lors que ces événements sont « prêts ». Bien que
« prêt » puisse vouloir dire n'importe quoi, dans notre cas nous allons nous baser sur un
temps d'horloge. Ce qui suit est une structure de contrôle qui ne contient aucune information
spécifique sur ce qu'elle contrôle. Voici tout d'abord l'interface qui décrit tout événement. C'est
une classe abstract plutôt qu'une interface parce que le
comportement par défaut est de réaliser le contrôle sur le temps, donc une partie de
l'implémentation peut y être incluse :
|
 |
 |
 |
//: c08:controller:Event.java // The common methods for any control event. package c08.controller;
abstract public class Event { private long evtTime; public Event(long eventTime) { evtTime = eventTime; } public boolean ready() { return System.currentTimeMillis() >= evtTime; } abstract public void action(); abstract public String description(); } ///:~
|
 |
//: c08:controller:Event.java // Les méthodes communes pour n'importe quel événement. package c08.controller;
abstract public class Event { private long evtTime; public Event(long eventTime) { evtTime = eventTime; } public boolean ready() { return System.currentTimeMillis() >= evtTime; } abstract public void action(); abstract public String description(); } ///:~
|
 |
 |
 |
The constructor simply captures the time
when you want the Event to run, while ready( ) tells you when
it’s time to run it. Of course, ready( ) could be overridden
in a derived class to base the Event on something other than
time.
|
 |
Le constructeur stocke simplement l'heure à laquelle on veut que
l'Event soit exécuté, tandis que ready() indique si c'est le
moment de le lancer. Bien sûr, ready() peut être redéfini dans une classe dérivée
pour baser les Event sur autre chose que le temps.
|
 |
 |
 |
action( ) is the method
that’s called when the Event is ready( ), and
description( ) gives textual information about the
Event.
|
 |
action() est la méthode appelée lorsque
l'Event est ready(), et description() donne des
informations (du texte) à propos de l'Event.
|
 |
 |
 |
The following file contains the actual
control framework that manages and fires events. The first class is really just
a “helper” class whose job is to hold Event objects. You can
replace it with any appropriate container, and in Chapter 9 you’ll
discover other containers that will do the trick without requiring you to write
this extra code:
|
 |
Le fichier suivant contient la structure de contrôle proprement dite qui
gère et déclenche les événements. La première classe est simplement une classe « d'aide »
dont le rôle consiste à stocker des objets Event. On peut la remplacer avec
n'importe quel conteneur plus approprié, et dans le Chapitre 9 nous verrons d'autres conteneurs qui
ne requerront pas ce code supplémentaire :
|
 |
 |
 |
//: c08:controller:Controller.java // Along with Event, the generic // framework for all control systems: package c08.controller;
// This is just a way to hold Event objects. class EventSet { private Event[] events = new Event[100]; private int index = 0; private int next = 0; public void add(Event e) { if(index >= events.length) return; // (In real life, throw exception) events[index++] = e; } public Event getNext() { boolean looped = false; int start = next; do { next = (next + 1) % events.length; // See if it has looped to the beginning: if(start == next) looped = true; // If it loops past start, the list // is empty: if((next == (start + 1) % events.length) && looped) return null; } while(events[next] == null); return events[next]; } public void removeCurrent() { events[next] = null; } }
public class Controller { private EventSet es = new EventSet(); public void addEvent(Event c) { es.add(c); } public void run() { Event e; while((e = es.getNext()) != null) { if(e.ready()) { e.action(); System.out.println(e.description()); es.removeCurrent(); } } } } ///:~
|
 |
//: c08:controller:Controller.java // Avec Event, la structure générique // pour tous les systèmes de contrôle : package c08.controller;
// Ceci est jsute une manière de stocker les objets Event. class EventSet { private Event[] events = new Event[100]; private int index = 0; private int next = 0; public void add(Event e) { if(index >= events.length) return; // (Normalement, générer une exception) events[index++] = e; } public Event getNext() { boolean looped = false; int start = next; do { next = (next + 1) % events.length; // Vérifie si on a fait le tour : if(start == next) looped = true; // Si on a fait le tour, c'est que la // liste est vide : if((next == (start + 1) % events.length) && looped) return null; } while(events[next] == null); return events[next]; } public void removeCurrent() { events[next] = null; } }
public class Controller { private EventSet es = new EventSet(); public void addEvent(Event c) { es.add(c); } public void run() { Event e; while((e = es.getNext()) != null) { if(e.ready()) { e.action(); System.out.println(e.description()); es.removeCurrent(); } } } } ///:~
|
 |
 |
 |
EventSet arbitrarily holds 100
Events. (If a “real” container from Chapter 9 is used here
you don’t need to worry about its maximum size, since it will resize
itself). The index is used to keep track of the next available space, and
next is used when you’re looking for the next Event in the
list, to see whether you’ve looped around. This is important during a call
to getNext( ), because Event objects are removed from the
list (using removeCurrent( )) once they’re run, so
getNext( ) will encounter holes in the list as it moves through
it.
|
 |
EventSet stocke arbitrairement 100 Events
(si un « vrai » conteneur du Chapitre 9 était utilisé ici, on n'aurait pas à se soucier à
propos de sa taille maximum, puisqu'il se redimensionnerait de lui-même). L'index
est utilisé lorsqu'on veut récupérer le prochain Event de la liste, pour voir si
on a fait le tour. Ceci est important pendant un appel à getNext(), parce que les
objets Event sont enlevés de la liste (avec removeCurrent()) une
fois exécutés, donc getNext() rencontrera des trous dans la liste lorsqu'il la
parcourra.
|
 |
 |
 |
Note that removeCurrent( )
doesn’t just set some flag indicating that the object is no longer in use.
Instead, it sets the reference to null. This is important because if the
garbage collector sees a reference
that’s still in use then it can’t clean up the object. If you think
your references might hang around (as they would here), then it’s a good
idea to set them to null to give the garbage collector permission to
clean them up.
|
 |
Notez que removeCurrent() ne positionne pas simplement un
flag indiquant que l'objet n'est plus utilisé. A la place, il positionne la référence à
null. C'est important car si le ramasse-miettes
rencontre une référence qui est encore utilisée il ne pourra pas nettoyer l'objet correspondant. Si
l'objet n'a plus de raison d'être (comme c'est le cas ici), il faut alors mettre leur référence à
null afin d'autoriser le ramasse-miettes à les nettoyer.
|
 |
 |
 |
Controller is where the actual
work goes on. It uses an EventSet to hold its Event objects, and
addEvent( ) allows you to add new events to this list. But the
important method is run( ). This method loops through the
EventSet, hunting for an Event object that’s
ready( ) to run. For each one it finds ready( ),
it calls the action( ) method, prints out the
description( ), and then removes the Event from the list.
|
 |
C'est dans Controller que tout le travail est effectué. Il
utiliser un EventSet pour stocker ses objets Event, et
addEvent() permet d'ajouter de nouveaux éléments à la liste. Mais la méthode
principale est run(). Cette méthode parcourt l'EventSet,
recherchant un objet Event qui soit ready(). Il appelle alors la
méthode action() pour cet objet, affiche sa description() et
supprime l'Event de la liste.
|
 |
 |
 |
Note that so far in this design you know
nothing about exactly what an Event does. And this is the crux of
the design; how it “separates the things that change from the things that
stay the same.” Or, to use my term, the
“vector of change” is
the different actions of the various kinds of Event objects, and you
express different actions by creating different Event
subclasses.
|
 |
Notez que jusqu'à présent dans la conception on ne sait rien sur ce que
fait exactement un Event. Et c'est le point fondamental de la conception :
comment elle « sépare les choses qui changent des choses qui ne bougent pas ». Ou, comme
je l'appelle, le « vecteur de changement » est constitué des différentes actions des
différents types d'objets Event, actions différentes réalisées en créant
différentes sous-classes d'Event.
|
 |
 |
 |
This is where inner classes come into
play. They allow two things:
|
 |
C'est là que les classes internes interviennent. Elles permettent deux
choses :
|
 |
 |
 |
- To create the entire
implementation of a control-framework application in a single class, thereby
encapsulating everything that’s unique about that implementation. Inner
classes are used to express the many different kinds of action( )
necessary to solve the problem. In addition, the following example uses
private inner classes so the implementation is completely hidden and can
be changed with
impunity.
- Inner
classes keep this implementation from becoming awkward, since you’re able
to easily access any of the members in the outer class. Without this ability the
code might become unpleasant enough that you’d end up seeking an
alternative.
|
 |
- Réaliser l'implémentation complète d'une application de structure de
contrôle dans une seule classe, encapsulant du même coup tout ce qui est unique dans cette
implémentation. Les classes internes sont utilisées pour décrire les différents types
d'action() nécessaires pour résoudre le problème. De plus, l'exemple suivant
utilise des classes internes private afin que l'implémentation soit complètement
cachée et puisse être changée en toute impunité.
- Empêcher que l'implémentation ne devienne trop lourde, puisqu'on est
capable d'accéder facilement à chacun des membres de la classe externe. Sans cette facilité, le
code deviendrait rapidement tellement confus qu'il faudrait chercher une autre
solution.
|
 |
 |
 |
Consider a
particular implementation of the control framework designed to control
greenhouse
functions[43]. Each
action is entirely different: turning lights, water, and thermostats on and off,
ringing bells, and restarting the system. But the control framework is designed
to easily isolate this different code. Inner classes allow you to have multiple
derived versions of the same base class, Event, within a single class.
For each type of action you inherit a new Event inner class, and write
the control code inside of action( ).
|
 |
Considérons une implémentation particulière de la structure de contrôle
conçue pour contrôler les fonctions d'une serre [43]. Chaque action est complètement différente : contrôler les
lumières, l'arrosage et la température, faire retentir des sonneries et relancer le système. Mais
la structure de contrôle est conçue pour isoler facilement ce code différent. Les classes internes
permettent d'avoir de multiples versions dérivées de la même classe de base (ici,
Event) à l'intérieur d'une seule et même classe. Pour chaque type d'action on crée
une nouvelle classe interne dérivée d'Event, et on écrit le code de contrôle dans
la méthode action().
|
 |
 |
 |
As is typical with an application
framework, the class GreenhouseControls is inherited from
Controller:
|
 |
Typiquement, la classe GreenhouseControls hérite de
Controller :
|
 |
 |
 |
//: c08:GreenhouseControls.java // This produces a specific application of the // control system, all in a single class. Inner // classes allow you to encapsulate different // functionality for each type of event. import c08.controller.*;
public class GreenhouseControls extends Controller { private boolean light = false; private boolean water = false; private String thermostat = "Day"; private class LightOn extends Event { public LightOn(long eventTime) { super(eventTime); } public void action() { // Put hardware control code here to // physically turn on the light. light = true; } public String description() { return "Light is on"; } } private class LightOff extends Event { public LightOff(long eventTime) { super(eventTime); } public void action() { // Put hardware control code here to // physically turn off the light. light = false; } public String description() { return "Light is off"; } } private class WaterOn extends Event { public WaterOn(long eventTime) { super(eventTime); } public void action() { // Put hardware control code here water = true; } public String description() { return "Greenhouse water is on"; } } private class WaterOff extends Event { public WaterOff(long eventTime) { super(eventTime); } public void action() { // Put hardware control code here water = false; } public String description() { return "Greenhouse water is off"; } } private class ThermostatNight extends Event { public ThermostatNight(long eventTime) { super(eventTime); } public void action() { // Put hardware control code here thermostat = "Night"; } public String description() { return "Thermostat on night setting"; } } private class ThermostatDay extends Event { public ThermostatDay(long eventTime) { super(eventTime); } public void action() { // Put hardware control code here thermostat = "Day"; } public String description() { return "Thermostat on day setting"; } } // An example of an action() that inserts a // new one of itself into the event list: private int rings; private class Bell extends Event { public Bell(long eventTime) { super(eventTime); } public void action() { // Ring every 2 seconds, 'rings' times: System.out.println("Bing!"); if(--rings > 0) addEvent(new Bell( System.currentTimeMillis() + 2000)); } public String description() { return "Ring bell"; } } private class Restart extends Event { public Restart(long eventTime) { super(eventTime); } public void action() { long tm = System.currentTimeMillis(); // Instead of hard-wiring, you could parse // configuration information from a text // file here: rings = 5; addEvent(new ThermostatNight(tm)); addEvent(new LightOn(tm + 1000)); addEvent(new LightOff(tm + 2000)); addEvent(new WaterOn(tm + 3000)); addEvent(new WaterOff(tm + 8000)); addEvent(new Bell(tm + 9000)); addEvent(new ThermostatDay(tm + 10000)); // Can even add a Restart object! addEvent(new Restart(tm + 20000)); } public String description() { return "Restarting system"; } } public static void main(String[] args) { GreenhouseControls gc = new GreenhouseControls(); long tm = System.currentTimeMillis(); gc.addEvent(gc.new Restart(tm)); gc.run(); } } ///:~
|
 |
//: c08:GreenhouseControls.java // Ceci est une application spécifique du // système de contrôle, le tout dans une seule classe. // Les classes internes permettent d'encapsuler des // fonctionnalités différentes pour chaque type d'Event. import c08.controller.*;
public class GreenhouseControls extends Controller { private boolean light = false; private boolean water = false; private String thermostat = "Day"; private class LightOn extends Event { public LightOn(long eventTime) { super(eventTime); } public void action() { // Placer ici du code de contrôle hardware pour // réellement allumer la lumière. light = true; } public String description() { return "Light is on"; } } private class LightOff extends Event { public LightOff(long eventTime) { super(eventTime); } public void action() { // Put hardware control code here to // physically turn off the light. light = false; } public String description() { return "Light is off"; } } private class WaterOn extends Event { public WaterOn(long eventTime) { super(eventTime); } public void action() { // Placer ici du code de contrôle hardware. water = true; } public String description() { return "Greenhouse water is on"; } } private class WaterOff extends Event { public WaterOff(long eventTime) { super(eventTime); } public void action() { // Placer ici du code de contrôle hardware. water = false; } public String description() { return "Greenhouse water is off"; } } private class ThermostatNight extends Event { public ThermostatNight(long eventTime) { super(eventTime); } public void action() { // Placer ici du code de contrôle hardware. thermostat = "Night"; } public String description() { return "Thermostat on night setting"; } } private class ThermostatDay extends Event { public ThermostatDay(long eventTime) { super(eventTime); } public void action() { // Placer ici du code de contrôle hardware. thermostat = "Day"; } public String description() { return "Thermostat on day setting"; } } // Un exemple d'une action() qui insère une nouvelle // instance de son type dans la liste d'Event : private int rings; private class Bell extends Event { public Bell(long eventTime) { super(eventTime); } public void action() { // Sonne toutes les 2 secondes, 'rings' fois : System.out.println("Bing!"); if(--rings > 0) addEvent(new Bell( System.currentTimeMillis() + 2000)); } public String description() { return "Ring bell"; } } private class Restart extends Event { public Restart(long eventTime) { super(eventTime); } public void action() { long tm = System.currentTimeMillis(); // Au lieu d'un codage en dur, on pourrait // récupérer les informations en parsant un // fichier de configuration : rings = 5; addEvent(new ThermostatNight(tm)); addEvent(new LightOn(tm + 1000)); addEvent(new LightOff(tm + 2000)); addEvent(new WaterOn(tm + 3000)); addEvent(new WaterOff(tm + 8000)); addEvent(new Bell(tm + 9000)); addEvent(new ThermostatDay(tm + 10000)); // On peut même ajouter un objet Restart ! addEvent(new Restart(tm + 20000)); } public String description() { return "Restarting system"; } } public static void main(String[] args) { GreenhouseControls gc = new GreenhouseControls(); long tm = System.currentTimeMillis(); gc.addEvent(gc.new Restart(tm)); gc.run(); } } ///:~
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |