diff --git a/README.md b/README.md index ddcd4a87e..2d6d3456a 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ A programming idiom is a means of expressing a recurring construct in one or mor * [Double Checked Locking](#double-checked-locking) * [Poison Pill](#poison-pill) * [Callback](#callback) +* [Lazy Loading](#lazy-loading) ## Abstract Factory [↑](#list-of-design-patterns) **Intent:** Provide an interface for creating families of related or dependent objects without specifying their concrete classes. @@ -482,6 +483,19 @@ A programming idiom is a means of expressing a recurring construct in one or mor **Applicability:** Use the Poison Pill idiom when * need to send signal from one thread/process to another to terminate +## Lazy Loading [↑](#list-of-design-patterns) +**Intent:** Lazy loading is a design pattern commonly used to defer initialization of an object until the point at which it is needed. It can contribute to efficiency in the program's operation if properly and appropriately used. + +![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/lazy-loading/etc/lazy-loading.png "Lazy Loading") + +**Applicability:** Use the Lazy Loading idiom when +* eager loading is expensive or the object to be loaded might not be need at all + +**Real world examples:** +* JPA annotations @OneToOne, @OneToMany, @ManyToOne, @ManyToMany and fetch = FetchType.LAZY + + + # Frequently asked questions **Q: What is the difference between State and Strategy patterns?** diff --git a/lazy-loading/etc/lazy-loading.png b/lazy-loading/etc/lazy-loading.png new file mode 100644 index 000000000..bec0a7afb Binary files /dev/null and b/lazy-loading/etc/lazy-loading.png differ diff --git a/lazy-loading/etc/lazy-loading.ucls b/lazy-loading/etc/lazy-loading.ucls new file mode 100644 index 000000000..9f4ccd1a4 --- /dev/null +++ b/lazy-loading/etc/lazy-loading.ucls @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lazy-loading/pom.xml b/lazy-loading/pom.xml new file mode 100644 index 000000000..64cb34822 --- /dev/null +++ b/lazy-loading/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.0-SNAPSHOT + + lazy-loading + + + junit + junit + test + + + diff --git a/lazy-loading/src/main/java/com/iluwatar/App.java b/lazy-loading/src/main/java/com/iluwatar/App.java new file mode 100644 index 000000000..c0f4b0591 --- /dev/null +++ b/lazy-loading/src/main/java/com/iluwatar/App.java @@ -0,0 +1,33 @@ +package com.iluwatar; + +/** + * + * Lazy loading idiom defers object creation until needed. + * + * This example shows different implementations of the pattern + * with increasing sophistication. + * + * Additional information and lazy loading flavours are described in + * http://martinfowler.com/eaaCatalog/lazyLoad.html + * + */ +public class App +{ + public static void main( String[] args ) { + + // Simple lazy loader - not thread safe + HolderNaive holderNaive = new HolderNaive(); + Heavy heavy = holderNaive.getHeavy(); + System.out.println("heavy=" + heavy); + + // Thread safe lazy loader, but with heavy synchronization on each access + HolderThreadSafe holderThreadSafe = new HolderThreadSafe(); + Heavy another = holderThreadSafe.getHeavy(); + System.out.println("another=" + another); + + // The most efficient lazy loader utilizing Java 8 features + Java8Holder java8Holder = new Java8Holder(); + Heavy next = java8Holder.getHeavy(); + System.out.println("next=" + next); + } +} diff --git a/lazy-loading/src/main/java/com/iluwatar/Heavy.java b/lazy-loading/src/main/java/com/iluwatar/Heavy.java new file mode 100644 index 000000000..534b83c9e --- /dev/null +++ b/lazy-loading/src/main/java/com/iluwatar/Heavy.java @@ -0,0 +1,19 @@ +package com.iluwatar; + +/** + * + * Heavy objects are expensive to create. + * + */ +public class Heavy { + + public Heavy() { + System.out.println("Creating Heavy ..."); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("... Heavy created"); + } +} diff --git a/lazy-loading/src/main/java/com/iluwatar/HolderNaive.java b/lazy-loading/src/main/java/com/iluwatar/HolderNaive.java new file mode 100644 index 000000000..b741cb7d5 --- /dev/null +++ b/lazy-loading/src/main/java/com/iluwatar/HolderNaive.java @@ -0,0 +1,23 @@ +package com.iluwatar; + +/** + * + * Simple implementation of the lazy loading idiom. + * However, this is not thread safe. + * + */ +public class HolderNaive { + + private Heavy heavy; + + public HolderNaive() { + System.out.println("HolderNaive created"); + } + + public Heavy getHeavy() { + if (heavy == null) { + heavy = new Heavy(); + } + return heavy; + } +} diff --git a/lazy-loading/src/main/java/com/iluwatar/HolderThreadSafe.java b/lazy-loading/src/main/java/com/iluwatar/HolderThreadSafe.java new file mode 100644 index 000000000..ef8282bd3 --- /dev/null +++ b/lazy-loading/src/main/java/com/iluwatar/HolderThreadSafe.java @@ -0,0 +1,24 @@ +package com.iluwatar; + +/** + * + * Same as HolderNaive but with added synchronization. + * This implementation is thread safe, but each {@link #getHeavy()} + * call costs additional synchronization overhead. + * + */ +public class HolderThreadSafe { + + private Heavy heavy; + + public HolderThreadSafe() { + System.out.println("HolderThreadSafe created"); + } + + public synchronized Heavy getHeavy() { + if (heavy == null) { + heavy = new Heavy(); + } + return heavy; + } +} diff --git a/lazy-loading/src/main/java/com/iluwatar/Java8Holder.java b/lazy-loading/src/main/java/com/iluwatar/Java8Holder.java new file mode 100644 index 000000000..c0241592a --- /dev/null +++ b/lazy-loading/src/main/java/com/iluwatar/Java8Holder.java @@ -0,0 +1,34 @@ +package com.iluwatar; + +import java.util.function.Supplier; + +/** + * + * This lazy loader is thread safe and more efficient than HolderThreadSafe. + * It utilizes Java 8 functional interface Supplier as Heavy factory. + * + */ +public class Java8Holder { + + private Supplier heavy = () -> createAndCacheHeavy(); + + public Java8Holder() { + System.out.println("Java8Holder created"); + } + + public Heavy getHeavy() { + return heavy.get(); + } + + private synchronized Heavy createAndCacheHeavy() { + class HeavyFactory implements Supplier { + private final Heavy heavyInstance = new Heavy(); + @Override + public Heavy get() { return heavyInstance; } + } + if (!HeavyFactory.class.isInstance(heavy)) { + heavy = new HeavyFactory(); + } + return heavy.get(); + } +} diff --git a/lazy-loading/src/test/java/com/iluwatar/AppTest.java b/lazy-loading/src/test/java/com/iluwatar/AppTest.java new file mode 100644 index 000000000..6db5ad214 --- /dev/null +++ b/lazy-loading/src/test/java/com/iluwatar/AppTest.java @@ -0,0 +1,12 @@ +package com.iluwatar; + +import org.junit.Test; + +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/pom.xml b/pom.xml index 8d5423d13..33b34296f 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,8 @@ property intercepting-filter poison-pill - + lazy-loading + @@ -73,4 +74,4 @@ - + \ No newline at end of file