diff --git a/factory/README.md b/factory/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9ab61b5796cd09cca291ed1862bb1057af1a797b --- /dev/null +++ b/factory/README.md @@ -0,0 +1,144 @@ +--- +layout: pattern +title: Factory +folder: factory +permalink: /patterns/factory/ +categories: Creational +tags: + - Gang of Four +--- + +## Also known as + +* Simple Factory +* Static Factory Method + +## Intent + +Providing a static method encapsulated in a class called factory, in order to hide the implementation logic and makes client code focus on usage rather then initialization new objects. + +## Explanation + +Real world example + +> Lets say we have a web application connected to SQLServer, but now we want to switch to Oracle. To do so without modifying existing source code, we need to implements Simple Factory pattern, in which a static method can be invoked to create connection to a given database. + +Wikipedia says + +> Factory is an object for creating other objects – formally a factory is a function or method that returns objects of a varying prototype or class. + +**Programmatic Example** + +We have an interface 'Car' and tow implementations 'Ford' and 'Ferrari'. + +```java +/** + * Car interface. + */ +public interface Car { + + public String getDescription(); + +} + +/** + * Ford implementation. + */ +public class Ford implements Car { + + static final String DESCRIPTION = "This is Ford."; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} + +/** + * Ferrari implementation. + */ +public class Ferrari implements Car { + + static final String DESCRIPTION = "This is Ferrari."; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} +``` + +Enumeration above represents types of cars that we support (Ford and Ferrari). + +```java +public enum CarType { + + /** + * Enumeration for different types of cars. + */ + FORD(Ford::new), + FERRARI(Ferrari::new); + + private final Supplier constructor; + + CarType(Supplier constructor) { + this.constructor = constructor; + } + + public Supplier getConstructor() { + return this.constructor; + } +} +``` +Then we have the static method 'getCar' to create car objects encapsulated in the factory class 'CarSimpleFactory'. + +```java +/** + * Factory of cars. + */ +public class CarsFactory { + + /** + * Factory method takes as parameter a car type and initiate the appropriate class. + */ + public static Car getCar(CarType type) { + return type.getConstructor().get(); + } +} +``` + +Now on the client code we can create different types of cars using the factory class. + +```java +var car1 = CarsFactory.getCar(CarType.FORD); +var car2 = CarsFactory.getCar(CarType.FERRARI); +LOGGER.info(car1.getDescription()); +LOGGER.info(car2.getDescription());; +``` + +Program output: + +```java +This is Ford. +This Ferrari. +``` +## Class Diagram +![alt text](./etc/factory.urm.png "Factory pattern class diagram") + +## Applicability +Use the Simple Factory pattern when you only care about the creation of a object, not how to create and manage it. + +## Pros +* Allows keeping all objects creation in one place and avoid of spreading 'new' key value across codebase. +* Allows to writs loosely coupled code. Some of its main advantages include better testability, easy-to-understand code, swappable components, scalability and isolated features. + +## Cons +* The code becomes more complicated than it should be. + +## Related patterns + +[Factory Method](https://java-design-patterns.com/patterns/factory-method/) +[Factory Kit](https://java-design-patterns.com/patterns/factory-kit/) +[Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/) + + diff --git a/factory/etc/factory.urm.png b/factory/etc/factory.urm.png new file mode 100644 index 0000000000000000000000000000000000000000..2ba39a571c4efd3d5cfd45854f0d26c843fafd8e Binary files /dev/null and b/factory/etc/factory.urm.png differ diff --git a/factory/etc/factory.urm.puml b/factory/etc/factory.urm.puml new file mode 100644 index 0000000000000000000000000000000000000000..9eded6328d456fb5047de0659ce30e90d5b7cfd2 --- /dev/null +++ b/factory/etc/factory.urm.puml @@ -0,0 +1,35 @@ +@startuml +package com.iluwatar.factory { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + interface Car { + + getDescription() : String {abstract} + } + class CarsFactory { + + CarsFactory() + + getCar(type : CarType) : Car {static} + } + ~enum CarType { + + FERRARI {static} + + FORD {static} + + valueOf(name : String) : CarType {static} + + values() : CarType[] {static} + } + class Ferrari { + ~ DESCRIPTION : String {static} + + Ferrari() + + getDescription() : String + } + class Ford { + ~ DESCRIPTION : String {static} + + Ford() + + getDescription() : String + } +} +CarType ..+ CarsFactory +Ferrari ..|> Car +Ford ..|> Car +@enduml \ No newline at end of file diff --git a/factory/pom.xml b/factory/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..fff262458d05c5743d7ad4ba190718ab7bc55817 --- /dev/null +++ b/factory/pom.xml @@ -0,0 +1,43 @@ + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.24.0-SNAPSHOT + + factory + + + org.junit.jupiter + junit-jupiter-engine + test + + + junit + junit + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.factory.App + + + + + + + + + \ No newline at end of file diff --git a/factory/src/main/java/com/iluwatar/factory/App.java b/factory/src/main/java/com/iluwatar/factory/App.java new file mode 100644 index 0000000000000000000000000000000000000000..22c50d86d9fa513dc463264f1d5a532dd71a1ead --- /dev/null +++ b/factory/src/main/java/com/iluwatar/factory/App.java @@ -0,0 +1,51 @@ +/* + * The MIT License + * Copyright © 2014-2019 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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Factory is an object for creating other objects, it providing Providing a static method to + * create and return objects of varying classes, in order to hide the implementation logic + * and makes client code focus on usage rather then objects initialization and management. + * + *

In this example the CarFactory is the factory class and it provides a static method to + * create different cars. + */ + +public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + /** + * Program main entry point. + */ + public static void main(String[] args) { + var car1 = CarsFactory.getCar(CarType.FORD); + var car2 = CarsFactory.getCar(CarType.FERRARI); + LOGGER.info(car1.getDescription()); + LOGGER.info(car2.getDescription()); + } +} diff --git a/factory/src/main/java/com/iluwatar/factory/Car.java b/factory/src/main/java/com/iluwatar/factory/Car.java new file mode 100644 index 0000000000000000000000000000000000000000..6f564233c8eb6ed86bd4470a7f46cbc93a5704fe --- /dev/null +++ b/factory/src/main/java/com/iluwatar/factory/Car.java @@ -0,0 +1,10 @@ +package com.iluwatar.factory; + +/** + * Car interface. + */ +public interface Car { + + public String getDescription(); + +} diff --git a/factory/src/main/java/com/iluwatar/factory/CarType.java b/factory/src/main/java/com/iluwatar/factory/CarType.java new file mode 100644 index 0000000000000000000000000000000000000000..5878025bdfbaa51e870d02ad6e8068c844c7d04f --- /dev/null +++ b/factory/src/main/java/com/iluwatar/factory/CarType.java @@ -0,0 +1,22 @@ +package com.iluwatar.factory; + +import java.util.function.Supplier; + +public enum CarType { + + /** + * Enumeration for different types of cars. + */ + FORD(Ford::new), + FERRARI(Ferrari::new); + + private final Supplier constructor; + + CarType(Supplier constructor) { + this.constructor = constructor; + } + + public Supplier getConstructor() { + return this.constructor; + } +} diff --git a/factory/src/main/java/com/iluwatar/factory/CarsFactory.java b/factory/src/main/java/com/iluwatar/factory/CarsFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..76b83d0bef2ed67e872f7c3fd54859965b747c50 --- /dev/null +++ b/factory/src/main/java/com/iluwatar/factory/CarsFactory.java @@ -0,0 +1,14 @@ +package com.iluwatar.factory; + +/** + * Factory of cars. + */ +public class CarsFactory { + + /** + * Factory method takes as parameter a car type and initiate the appropriate class. + */ + public static Car getCar(CarType type) { + return type.getConstructor().get(); + } +} diff --git a/factory/src/main/java/com/iluwatar/factory/Ferrari.java b/factory/src/main/java/com/iluwatar/factory/Ferrari.java new file mode 100644 index 0000000000000000000000000000000000000000..590167f81f128d299082886a76166210e55cb6d6 --- /dev/null +++ b/factory/src/main/java/com/iluwatar/factory/Ferrari.java @@ -0,0 +1,14 @@ +package com.iluwatar.factory; + +/** + * Ferrari implementation. + */ +public class Ferrari implements Car { + + static final String DESCRIPTION = "This is Ferrari."; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} diff --git a/factory/src/main/java/com/iluwatar/factory/Ford.java b/factory/src/main/java/com/iluwatar/factory/Ford.java new file mode 100644 index 0000000000000000000000000000000000000000..13ae7a49e7bf13d641b1260f9527e721a212747c --- /dev/null +++ b/factory/src/main/java/com/iluwatar/factory/Ford.java @@ -0,0 +1,14 @@ +package com.iluwatar.factory; + +/** + * Ford implementation. + */ +public class Ford implements Car { + + static final String DESCRIPTION = "This is Ford."; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} diff --git a/factory/src/test/java/com/iluwatar/factory/AppTest.java b/factory/src/test/java/com/iluwatar/factory/AppTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d29ffb6a2c8cfddc38bf34f5f3293d8532f0166e --- /dev/null +++ b/factory/src/test/java/com/iluwatar/factory/AppTest.java @@ -0,0 +1,14 @@ +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/src/test/java/com/iluwatar/factory/CarsFactoryTest.java b/factory/src/test/java/com/iluwatar/factory/CarsFactoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fc823c6d48982a3f68c14e1a64cea1a864fb7e43 --- /dev/null +++ b/factory/src/test/java/com/iluwatar/factory/CarsFactoryTest.java @@ -0,0 +1,15 @@ +package com.iluwatar.factory; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class CarsFactoryTest { + + @Test + void shouldReturnFerrariInstance() { + final var ferrari = CarsFactory.getCar(CarType.FERRARI); + assertTrue(ferrari instanceof Ferrari); + } + +} diff --git a/pom.xml b/pom.xml index 6d779700f9f589931e79784a9bb4341ece808519..da3084ab2c513546ee189479d1778d55f6ad758a 100644 --- a/pom.xml +++ b/pom.xml @@ -195,6 +195,7 @@ arrange-act-assert transaction-script filterer + factory