From 3b1f82a2a4378d48058af840417b63d9c6ead211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 13 Jun 2021 18:57:21 +0300 Subject: [PATCH] Update flyweight --- factory-method/README.md | 2 +- flyweight/README.md | 100 ++++++++++++++---- .../com/iluwatar/flyweight/AlchemistShop.java | 8 +- .../main/java/com/iluwatar/flyweight/App.java | 4 +- 4 files changed, 85 insertions(+), 29 deletions(-) diff --git a/factory-method/README.md b/factory-method/README.md index 1d1cd87f4..813a68445 100644 --- a/factory-method/README.md +++ b/factory-method/README.md @@ -7,7 +7,7 @@ categories: Creational language: en tags: - Extensibility - - Gang Of Four + - Gang of Four --- ## Also known as diff --git a/flyweight/README.md b/flyweight/README.md index 38af1f2dd..79ca511ba 100644 --- a/flyweight/README.md +++ b/flyweight/README.md @@ -6,7 +6,7 @@ permalink: /patterns/flyweight/ categories: Structural language: en tags: - - Gang Of Four + - Gang of Four - Performance --- @@ -16,11 +16,11 @@ Use sharing to support large numbers of fine-grained objects efficiently. ## Explanation -Real world example +Real-world example > Alchemist's shop has shelves full of magic potions. Many of the potions are the same so there is -> no need to create new object for each of them. Instead one object instance can represent multiple -> shelf items so memory footprint remains small. +> no need to create a new object for each of them. Instead, one object instance can represent +> multiple shelf items so the memory footprint remains small. In plain words @@ -36,7 +36,7 @@ Wikipedia says **Programmatic example** -Translating our alchemist shop example from above. First of all we have different potion types: +Translating our alchemist shop example from above. First of all, we have different potion types: ```java public interface Potion { @@ -104,27 +104,81 @@ public class PotionFactory { } ``` -And it can be used as below: +`AlchemistShop` contains two shelves of magic potions. The potions are created using the +aforementioned `PotionFactory`. ```java -var factory = new PotionFactory(); -factory.createPotion(PotionType.INVISIBILITY).drink(); // You become invisible. (Potion=6566818) -factory.createPotion(PotionType.HEALING).drink(); // You feel healed. (Potion=648129364) -factory.createPotion(PotionType.INVISIBILITY).drink(); // You become invisible. (Potion=6566818) -factory.createPotion(PotionType.HOLY_WATER).drink(); // You feel blessed. (Potion=1104106489) -factory.createPotion(PotionType.HOLY_WATER).drink(); // You feel blessed. (Potion=1104106489) -factory.createPotion(PotionType.HEALING).drink(); // You feel healed. (Potion=648129364) +@Slf4j +public class AlchemistShop { + + private final List topShelf; + private final List bottomShelf; + + public AlchemistShop() { + var factory = new PotionFactory(); + topShelf = List.of( + factory.createPotion(PotionType.INVISIBILITY), + factory.createPotion(PotionType.INVISIBILITY), + factory.createPotion(PotionType.STRENGTH), + factory.createPotion(PotionType.HEALING), + factory.createPotion(PotionType.INVISIBILITY), + factory.createPotion(PotionType.STRENGTH), + factory.createPotion(PotionType.HEALING), + factory.createPotion(PotionType.HEALING) + ); + bottomShelf = List.of( + factory.createPotion(PotionType.POISON), + factory.createPotion(PotionType.POISON), + factory.createPotion(PotionType.POISON), + factory.createPotion(PotionType.HOLY_WATER), + factory.createPotion(PotionType.HOLY_WATER) + ); + } + + public final List getTopShelf() { + return List.copyOf(this.topShelf); + } + + public final List getBottomShelf() { + return List.copyOf(this.bottomShelf); + } + + public void drinkPotions() { + LOGGER.info("Drinking top shelf potions\n"); + topShelf.forEach(Potion::drink); + LOGGER.info("Drinking bottom shelf potions\n"); + bottomShelf.forEach(Potion::drink); + } +} +``` + +In our scenario, a brave visitor enters the alchemist shop and drinks all the potions. + +```java +// create the alchemist shop with the potions +var alchemistShop = new AlchemistShop(); +// a brave visitor enters the alchemist shop and drinks all the potions +alchemistShop.drinkPotions(); ``` Program output: ```java -You become invisible. (Potion=6566818) -You feel healed. (Potion=648129364) -You become invisible. (Potion=6566818) -You feel blessed. (Potion=1104106489) -You feel blessed. (Potion=1104106489) -You feel healed. (Potion=648129364) +Drinking top shelf potions +You become invisible. (Potion=1509514333) +You become invisible. (Potion=1509514333) +You feel strong. (Potion=739498517) +You feel healed. (Potion=125130493) +You become invisible. (Potion=1509514333) +You feel strong. (Potion=739498517) +You feel healed. (Potion=125130493) +You feel healed. (Potion=125130493) +Drinking bottom shelf potions +Urgh! This is poisonous. (Potion=166239592) +Urgh! This is poisonous. (Potion=166239592) +Urgh! This is poisonous. (Potion=166239592) +You feel blessed. (Potion=991505714) +You feel blessed. (Potion=991505714) ``` ## Class diagram @@ -138,13 +192,13 @@ Flyweight pattern when all of the following are true: * An application uses a large number of objects. * Storage costs are high because of the sheer quantity of objects. -* Most object state can be made extrinsic. -* Many groups of objects may be replaced by relatively few shared objects once extrinsic state is -removed. +* Most of the object state can be made extrinsic. +* Many groups of objects may be replaced by relatively few shared objects once the extrinsic state + is removed. * The application doesn't depend on object identity. Since flyweight objects may be shared, identity tests will return true for conceptually distinct objects. -## Real world examples +## Known uses * [java.lang.Integer#valueOf(int)](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf%28int%29) and similarly for Byte, Character and other wrapped types. diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java b/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java index 135712718..f96a9027c 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java @@ -78,12 +78,12 @@ public class AlchemistShop { } /** - * Enumerate potions. + * Drink all the potions. */ - public void enumerate() { - LOGGER.info("Enumerating top shelf potions\n"); + public void drinkPotions() { + LOGGER.info("Drinking top shelf potions"); topShelf.forEach(Potion::drink); - LOGGER.info("Enumerating bottom shelf potions\n"); + LOGGER.info("Drinking bottom shelf potions"); bottomShelf.forEach(Potion::drink); } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/App.java b/flyweight/src/main/java/com/iluwatar/flyweight/App.java index 1982d376e..ec08872bb 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/App.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/App.java @@ -43,7 +43,9 @@ public class App { * @param args command line args */ public static void main(String[] args) { + // create the alchemist shop with the potions var alchemistShop = new AlchemistShop(); - alchemistShop.enumerate(); + // a brave visitor enters the alchemist shop and drinks all the potions + alchemistShop.drinkPotions(); } }