Je lis la troisième édition d'Essential Java pour tenter de mettre à jour mes connaissances de Java, et le modèle Flyweight
apparaît. Au fait, je n'ai jamais fait ce modèle correctement, alors j'ai décidé de le comprendre et de l'essayer.
Le modèle Flyweight est l'un des modèles de conception du GoF et est un modèle pour résoudre les problèmes suivants.
Je ne sais pas si c'est tout.
En regardant la séquence, cela ressemble à un modèle de mise en cache. En regardant l'élément d'historique, il semble qu'il a été utilisé dans l'éditeur de document, et si les informations de police, par exemple, l'alphabet est de 26 caractères, si une instance du caractère est créée à chaque fois, un grand nombre d'instances sera généré. Par conséquent, cela semble être un mécanisme pour créer seulement 26 instances, les mettre en cache et les renvoyer.
Faisons un échantillon et comprenons-le. Bref, c'est un cache d'usine, donc c'est facile. Le thème de cette fois est un peu la force brute, mais c'est une application qui renvoie le son de l'endroit lorsque vous entrez le numéro de corde et le numéro de frette de la guitare. Puisqu'il n'y a que 12 sons, 12 instances devraient suffire. Essayez d'utiliser Sound
comme objet Flyweight
dans le diagramme de modèle de conception.
Sound.java : Flyweight
package com.company;
public interface Sound {
void Play();
}
SoundImpl : Flywieght1
package com.company;
public class SoundImpl implements Sound{
private String note;
public SoundImpl(String note) {
this.note = note;
}
@Override
public void Play() {
System.out.println(note + "- ♪");
}
public String getNote() {
return note;
}
}
SoundFactory : FlywieghtFactory
package com.company;
import java.util.HashMap;
public class SoundFactory {
private static Map<String, Sound> sounds = new HashMap<String, Sound>();
public static Sound getSound(String note) {
if (sounds.containsKey(note)){
return sounds.get(note);
} else {
Sound sound = new SoundImpl(note);
sounds.put(note, sound);
return sound;
}
}
}
Le Main a gonflé et n'était pas cool, mais de toute façon, j'ai quelque chose qui a fonctionné. Le point simple est de donner à Factory une carte et de la mettre en cache. C'est facile.
Main.java
package com.company;
import java.util.HashMap;
public class Main {
private static HashMap<Integer, Integer> stringMap = new HashMap<Integer, Integer>();
private static HashMap<Integer, String> notes = new HashMap<Integer, String>();
private static void setup() {
stringMap.put(1, 4);
stringMap.put(2, 11);
stringMap.put(3, 7);
stringMap.put(4, 2);
stringMap.put(5, 9);
stringMap.put(6, 4);
notes.put(0, "C");
notes.put(1, "C#");
notes.put(2, "D");
notes.put(3, "D#");
notes.put(4, "E");
notes.put(5, "F");
notes.put(6, "F#");
notes.put(7, "G");
notes.put(8, "G#");
notes.put(9, "A");
notes.put(10, "A#");
notes.put(11, "B");
}
public static void main(String[] args) {
setup();
while(true) {
java.io.Console con = System.console();
if (con != null) {
String input = con.readLine("string:fret:");
if (input.contains("exit")) {
System.out.println("Closing ...");
System.exit(0);
} else {
String[] stringFret = input.split(":");
int openNote = stringMap.get(Integer.parseInt(stringFret[0]));
int fret = Integer.parseInt(stringFret[1]);
int note = openNote + fret;
if (note >= 12) {
note = note - 12;
}
String noteString = notes.get(new Integer(note));
Sound sound = SoundFactory.getSound(noteString);
sound.Play();
}
}
}
}
}
Comme la partie principale était facile, je ne suis pas très familier avec Java, j'ai donc décidé de prendre des mesures. Que dois-je faire s'il est dans un état concurrent? HashMap
ne semble pas être sûr pour les threads. Si vous voulez le rendre sûr pour les threads, vous pouvez utiliser HashTable
ou ConcurrentHashMap
. Quelle est la différence?
HashMap
et ConcurrentHashMap
est de savoir s'il est thread-safe ou non. En d'autres termes, peut-il être utilisé dans un environnement concurrent? Il ne peut pas fournir le niveau de synchronisation fourni par HashTable
, mais c'est assez bon pour une utilisation pratique.HashMap
renvoie presque la même collection que HashTable
lorsqu'il est enveloppé dans Collections.synchronizedMap
. Autrement dit, tous les événements de mise à jour verrouillent l'intégralité de la Map
. Dans le cas de ConcurrentHashMap
, Thread Safe est conçu pour partitionner la totalité de la carte et n'en verrouiller qu'une partie.ConcurrentHashMap
est plus évolutif et a de meilleures performances que Synchronized HashMap
. Dans un environnement monothread, «HashMap» surpasse «ConcurrentHashMap».En regardant ces comparaisons, il semble qu'il existe de nombreux cas où il est clairement approprié d'utiliser ConcurrentHashMap
. Implémentons-le.
Cela semble facile.
Micronaut install
Dans mon environnement, la source de chocolatey
a été réécrite, je vais donc la restaurer par défaut. Il peut être installé en démarrant PowerShell avec des privilèges d'administrateur.
$ choco isntall micronaut -s https://chocolatey.org/api/v2/
Generate project via template
Vous pouvez générer un projet Maven comme ceci:
$ mn create-app flyweight-server --build maven
Tout d'abord, cela fera démarrer le serveur. Le point auquel j'étais accro était que j'avais écrit du code en utilisant IntelliJ, mais le terminal JAVA_HOME n'était pas défini, et j'ai reçu le message d'erreur Causé par: java.lang.IllegalArgumentException: version cible invalide: 11
. C'est sorti. À l'origine, seul le JDK Java8 était dans le chemin, donc c'est un problème, mais je me suis rappelé que les bases de Java sont de définir JAVA_HOME et de passer% JAVA_HOME% / bin dans le chemin. Sinon, maven ne fonctionnera pas correctement.
$ cd flywieght-server
$ mvn clean package
$ java -jar .\target\flyweight-server-0.1.jar
J'ajouterai le contrôleur. Micronaut semble prendre en charge diverses fonctions comme une version légère de Spring. Je voulais juste Http Server cette fois, alors je vais regarder le manuel et écrire le code. Pour statuer avec le contrôleur, il semble que vous ayez juste à écrire le contrôleur. C'est très facile.
FlyweightController.java
package flyweight.server;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
@Controller("/flyweight")
public class FlyweightController {
@Get(value = "/{string}/{fret}", produces = MediaType.TEXT_PLAIN)
public String index(Integer string, Integer fret){
return "String: " + string + " Fret: "+ fret;
}
}
Maintenant que cela fonctionne, je comprends ce que le contrôleur veut savoir. Ensuite, modifions l'application Flyweight. Je pense que cela fonctionne multithread, alors utilisons le ConcurrentHashMap
plus tôt.
Comme c'était plus court qu'avant. La méthode computeIfAbsent
accepte désormais les fonctions et garantit le comportement atomique. C'est la même atmosphère que le C # ConcurrentDictionary. Donc, s'il n'existe pas, il en fera un nouveau et le retournera, sinon il retournera l'existant. Le meilleur!
SoundFactory.java
package flyweight.server;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class SoundFactory {
private static ConcurrentMap<String, Sound> sounds = new ConcurrentHashMap<String, Sound>();
public static Sound getSound(String note) {
return sounds.computeIfAbsent(note, n -> new SoundImpl(n));
}
}
Vue d'ensemble
FlyweightController.java
package flyweight.server;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
@Controller("/flyweight")
public class FlyweightController {
@Get(value = "/{string}/{fret}", produces = MediaType.TEXT_PLAIN)
public String index(Integer string, Integer fret){
Integer openNote = Constants.stringMap.get(string.intValue());
int note = openNote.intValue() + fret.intValue();
if (note >= 12) {
note = note - 12;
}
Sound sound = SoundFactory.getSound(Constants.notes.get(note));
return "String: " + string + " Fret: "+ fret + " Sound: " + sound.Play();
}
}
Constants.java
package flyweight.server;
import java.util.HashMap;
public class Constants {
static HashMap<Integer, Integer> stringMap = new HashMap<Integer, Integer>();
static HashMap<Integer, String> notes = new HashMap<Integer, String>();
static void setup() {
stringMap.put(1, 4);
stringMap.put(2, 11);
stringMap.put(3, 7);
stringMap.put(4, 2);
stringMap.put(5, 9);
stringMap.put(6, 4);
notes.put(0, "C");
notes.put(1, "C#");
notes.put(2, "D");
notes.put(3, "D#");
notes.put(4, "E");
notes.put(5, "F");
notes.put(6, "F#");
notes.put(7, "G");
notes.put(8, "G#");
notes.put(9, "A");
notes.put(10, "A#");
notes.put(11, "B");
}
}
Sound.java
package flyweight.server;
public interface Sound {
String Play();
}
SoundImpl.java
package flyweight.server;
public class SoundImpl implements Sound {
private String note;
public SoundImpl(String note) {
this.note = note;
}
public String Play() {
return this.note + "- note";
}
public String getNote() {
return this.note;
}
}
Application.java
package flyweight.server;
import io.micronaut.runtime.Micronaut;
public class Application {
public static void main(String[] args) {
Constants.setup();
Micronaut.run(Application.class, args);
}
}
[INFO] Replacing original artifact with shaded artifact.
[INFO] Replacing C:\Users\tsushi\Code\java\spike\micronaut\flyweight-server\target\flyweight-server-0.1.jar with C:\Users\tsushi\Code\java\spike\micronaut\flyweight-server\target\flyweight-server-0.1-shaded.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.444 s
[INFO] Finished at: 2020-10-26T14:16:48-07:00
[INFO] ------------------------------------------------------------------------
PS C:\Users\tsushi\Code\java\spike\micronaut\flyweight-server> java -jar .\target\flyweight-server-0.1.jar
←[36m14:17:24.022←[0;39m ←[1;30m[main]←[0;39m ←[34mINFO ←[0;39m ←[35mio.micronaut.runtime.Micronaut←[0;39m - Startup completed in 1154ms. Server Running: http://localhost:8080
Je suis content d'avoir appris à la légère le modèle «Flyweight» et le «ConcurrentHashMap» aujourd'hui.
Resource
Recommended Posts