diff --git a/factory-enum/README.md b/factory-enum/README.md new file mode 100644 index 000000000000..50473e392fa0 --- /dev/null +++ b/factory-enum/README.md @@ -0,0 +1,126 @@ +--- +title: "Factory Enum Pattern in Java: Simplifying Object Access" +shortTitle: Factory Enum +description: "Learn the Factory Enum Pattern in Java using practical examples. Understand how to use enums to manage object instantiation in a clean, scalable, and singleton-safe way." +category: Creational +language: en +tag: + - Abstraction + - Enum Singleton + - Gang of Four + - Factory Pattern + - Object Instantiation +--- + +## Intent of Factory Enum Pattern + +The **Factory Enum Pattern** is a creational pattern that leverages Java's `enum` type to manage object instantiation. It provides a centralized, type-safe, and singleton-friendly way to access various implementations of a common interface. + +This pattern is ideal when the set of possible objects is known and fixed (e.g., file processors, coin types, document types). + +## Real-World Analogy + +> Imagine a toolbox where each tool has a fixed slot. You don’t create a new tool each time — you just access the correct one from the right compartment. Similarly, with Factory Enum, each `enum` constant maps to a specific object instance that can be reused or constructed once. + +## Programmatic Example Using FileProcessor + +In this example, we want to process different file types: Excel and PDF. Each processor implements the `FileProcessor` interface. + +### FileProcessor Interface + +```java +public interface FileProcessor { + String getDescription(); +} +``` +Implementations +```java +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ExcelProcessor implements FileProcessor { + + static final String DESCRIPTION = "This is an Excel processor."; + + @Override + public String getDescription() { + return DESCRIPTION; + } + + public static class InstanceHolder { + public static final ExcelProcessor INSTANCE = new ExcelProcessor(); + } +} +``` +```java +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class PDFProcessor implements FileProcessor { + + static final String DESCRIPTION = "This is a PDF processor."; + + @Override + public String getDescription() { + return DESCRIPTION; + } + + public static class InstanceHolder { + public static final PDFProcessor INSTANCE = new PDFProcessor(); + } +} +``` +Factory Enum: FileProcessorType +```java +@RequiredArgsConstructor +@Getter +public enum FileProcessorType { + EXCEL(ExcelProcessor.InstanceHolder.INSTANCE), + PDF(PDFProcessor.InstanceHolder.INSTANCE); + + private final FileProcessor instance; +} +``` +Client Code +```java +public static void main(String[] args) { + LOGGER.info("Start processing files."); + var excel = FileProcessorType.EXCEL.getInstance(); + var pdf = FileProcessorType.PDF.getInstance(); + LOGGER.info(excel.getDescription()); + LOGGER.info(pdf.getDescription()); +} +``` +Output +``` +INFO: Start processing files. +INFO: This is an Excel processor. +INFO: This is a PDF processor. +``` +## When to Use the Factory Enum Pattern + +* When object types are known in advance and won't change frequently. + +* When you want singleton instances per type. + +* To avoid traditional factory classes and keep the logic self-contained within an enum. + +* When readability and simplicity are preferred over extensibility. + +## Benefits +* Type-safe and readable – enum provides compiler-checked safety. +* Singleton-friendly – each instance is created once and reused. +* No need for external factory class – the enum holds both the type and instance. +* Thread-safe by nature of enum and static holders. + +## Trade-offs +* Not suitable for dynamic or extensible object types. +* Violates the Open/Closed Principle if new types are added frequently. +* Slightly unconventional compared to traditional Factory pattern. + +## Related Patterns +* [Traditional Factory Method](https://java-design-patterns.com/patterns/factory/): Separates creation logic into a factory class. +* [Enum Singleton](https://www.baeldung.com/java-singleton#enum-singleton): Ensures one instance per type via enum. +* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/): Can be considered a kind of Factory that works with groups of products. +* [Service Locator](https://java-design-patterns.com/patterns/service-locator/): If you combine enum with lookup behavior. + +## References and Credits +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0Rk5y) +* [Effective Java](https://amzn.to/4cGk2Jz) +* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/3UpTLrG) \ No newline at end of file diff --git a/factory-enum/etc/factory-sequence-diagram.png b/factory-enum/etc/factory-sequence-diagram.png new file mode 100644 index 000000000000..260bea92f247 Binary files /dev/null and b/factory-enum/etc/factory-sequence-diagram.png differ diff --git a/factory-enum/etc/factory.urm.png b/factory-enum/etc/factory.urm.png new file mode 100644 index 000000000000..3ede4cebc240 Binary files /dev/null and b/factory-enum/etc/factory.urm.png differ diff --git a/factory-enum/etc/factory.urm.puml b/factory-enum/etc/factory.urm.puml new file mode 100644 index 000000000000..3fd937aaf691 --- /dev/null +++ b/factory-enum/etc/factory.urm.puml @@ -0,0 +1,38 @@ +@startuml +package com.iluwatar.factory { + + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + + interface FileProcessor { + + getDescription() : String {abstract} + } + + enum FileProcessorType { + + EXCEL {static} + + PDF {static} + - instance : FileProcessor + + getInstance() : FileProcessor + + valueOf(name : String) : FileProcessorType {static} + + values() : FileProcessorType[] {static} + } + + class ExcelProcessor { + ~ DESCRIPTION : String {static} + + ExcelProcessor() + + getDescription() : String + } + + class PDFProcessor { + ~ DESCRIPTION : String {static} + + PDFProcessor() + + getDescription() : String + } +} + +ExcelProcessor ..|> FileProcessor +PDFProcessor ..|> FileProcessor +@enduml \ No newline at end of file diff --git a/factory-enum/pom.xml b/factory-enum/pom.xml new file mode 100644 index 000000000000..02fd31faa661 --- /dev/null +++ b/factory-enum/pom.xml @@ -0,0 +1,72 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + factory-enum + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.factory.App + + + + + + + + + diff --git a/factory-enum/src/main/java/com/iluwatar/factory/App.java b/factory-enum/src/main/java/com/iluwatar/factory/App.java new file mode 100644 index 000000000000..4f0c8eeae53d --- /dev/null +++ b/factory-enum/src/main/java/com/iluwatar/factory/App.java @@ -0,0 +1,49 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory; + +import lombok.extern.slf4j.Slf4j; + +/** + * The Factory Enum Pattern is a creational design pattern that leverages Java enums to encapsulate + * the creation logic of different object types. It promotes cleaner code by removing the need for + * separate factory classes and centralizing instantiation within type-safe enum constants. + * + *

In this example, different file processors (Excel, PDF) implement the same interface. The + * FileProcessorType enum holds the corresponding instance for each processor, allowing the client + * code to retrieve them in a clean and consistent way. + */ +@Slf4j +public class App { + + /** Program main entry point. */ + public static void main(String[] args) { + LOGGER.info("The alchemist begins his work."); + var excelProcessor = FileProcessorType.EXCEL.getInstance(); + var pdfProcessor = FileProcessorType.PDF.getInstance(); + LOGGER.info(excelProcessor.getDescription()); + LOGGER.info(pdfProcessor.getDescription()); + } +} diff --git a/factory-enum/src/main/java/com/iluwatar/factory/ExcelProcessor.java b/factory-enum/src/main/java/com/iluwatar/factory/ExcelProcessor.java new file mode 100644 index 000000000000..5a4a6ba5a217 --- /dev/null +++ b/factory-enum/src/main/java/com/iluwatar/factory/ExcelProcessor.java @@ -0,0 +1,45 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** ExcelProcessor implementation. */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ExcelProcessor implements FileProcessor { + + static final String DESCRIPTION = "This is a excel processor."; + + @Override + public String getDescription() { + return DESCRIPTION; + } + + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class InstanceHolder { + public static final ExcelProcessor INSTANCE = new ExcelProcessor(); + } +} diff --git a/factory-enum/src/main/java/com/iluwatar/factory/FileProcessor.java b/factory-enum/src/main/java/com/iluwatar/factory/FileProcessor.java new file mode 100644 index 000000000000..04e40177c0d7 --- /dev/null +++ b/factory-enum/src/main/java/com/iluwatar/factory/FileProcessor.java @@ -0,0 +1,31 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory; + +/** FileProcessor interface. */ +public interface FileProcessor { + + String getDescription(); +} diff --git a/factory-enum/src/main/java/com/iluwatar/factory/FileProcessorType.java b/factory-enum/src/main/java/com/iluwatar/factory/FileProcessorType.java new file mode 100644 index 000000000000..9bdf84652d9b --- /dev/null +++ b/factory-enum/src/main/java/com/iluwatar/factory/FileProcessorType.java @@ -0,0 +1,38 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** Enumeration for different types of fileProcessor. */ +@RequiredArgsConstructor +@Getter +public enum FileProcessorType { + EXCEL(ExcelProcessor.InstanceHolder.INSTANCE), + PDF(PDFProcessor.InstanceHolder.INSTANCE); + + private final FileProcessor instance; +} diff --git a/factory-enum/src/main/java/com/iluwatar/factory/PDFProcessor.java b/factory-enum/src/main/java/com/iluwatar/factory/PDFProcessor.java new file mode 100644 index 000000000000..8433192167dc --- /dev/null +++ b/factory-enum/src/main/java/com/iluwatar/factory/PDFProcessor.java @@ -0,0 +1,45 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** PDFProcessor implementation. */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class PDFProcessor implements FileProcessor { + + static final String DESCRIPTION = "This is a PDF processor."; + + @Override + public String getDescription() { + return DESCRIPTION; + } + + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class InstanceHolder { + public static final PDFProcessor INSTANCE = new PDFProcessor(); + } +} diff --git a/factory-enum/src/test/java/com/iluwatar/factory/AppTest.java b/factory-enum/src/test/java/com/iluwatar/factory/AppTest.java new file mode 100644 index 000000000000..702481525d45 --- /dev/null +++ b/factory-enum/src/test/java/com/iluwatar/factory/AppTest.java @@ -0,0 +1,37 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class AppTest { + + @Test + void shouldExecuteWithoutExceptions() { + assertDoesNotThrow(() -> App.main(new String[] {})); + } +} diff --git a/factory-enum/src/test/java/com/iluwatar/factory/FileProcessorTest.java b/factory-enum/src/test/java/com/iluwatar/factory/FileProcessorTest.java new file mode 100644 index 000000000000..870a688fbc96 --- /dev/null +++ b/factory-enum/src/test/java/com/iluwatar/factory/FileProcessorTest.java @@ -0,0 +1,56 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.factory; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class FileProcessorTest { + + @Test + void shouldReturnGoldCoinInstance() { + final var goldCoin = FileProcessorType.PDF.getInstance(); + assertInstanceOf(PDFProcessor.class, goldCoin); + } + + @Test + void shouldGoldCoinMessage() { + final var goldCoin = FileProcessorType.PDF.getInstance(); + assertEquals(PDFProcessor.DESCRIPTION, goldCoin.getDescription()); + } + + @Test + void shouldReturnCopperCoinInstance() { + final var copperCoin = FileProcessorType.EXCEL.getInstance(); + assertInstanceOf(ExcelProcessor.class, copperCoin); + } + + @Test + void shouldCopperCoinMessage() { + final var copperCoin = FileProcessorType.EXCEL.getInstance(); + assertEquals(ExcelProcessor.DESCRIPTION, copperCoin.getDescription()); + } +} diff --git a/pom.xml b/pom.xml index 8337c97966da..34580d24ca4b 100644 --- a/pom.xml +++ b/pom.xml @@ -130,6 +130,7 @@ extension-objects facade factory + factory-enum factory-kit factory-method fanout-fanin