Update factory example

This commit is contained in:
Ilkka Seppälä 2021-06-13 10:50:29 +03:00
parent 0d41af972d
commit 588b106ada
No known key found for this signature in database
GPG Key ID: 31B7C8F5CC412ECB
10 changed files with 85 additions and 87 deletions

View File

@ -16,16 +16,17 @@ tags:
## 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.
Providing a static method encapsulated in a class called the factory, to hide the implementation
logic and make client code focus on usage rather than initializing new objects.
## Explanation
Real world example
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.
> Imagine an alchemist who is about to manufacture coins. The alchemist must be able to create both
> gold and copper coins and switching between them must be possible without modifying the existing
> source code. The factory pattern makes it possible by providing a static construction method which
> can be called with relevant parameters.
Wikipedia says
@ -34,16 +35,16 @@ Wikipedia says
**Programmatic Example**
We have an interface `Car` and two implementations `Ford` and `Ferrari`.
We have an interface `Coin` and two implementations `GoldCoin` and `CopperCoin`.
```java
public interface Car {
public interface Coin {
String getDescription();
}
public class Ford implements Car {
public class GoldCoin implements Coin {
static final String DESCRIPTION = "This is Ford.";
static final String DESCRIPTION = "This is a gold coin.";
@Override
public String getDescription() {
@ -51,9 +52,9 @@ public class Ford implements Car {
}
}
public class Ferrari implements Car {
public class CopperCoin implements Coin {
static final String DESCRIPTION = "This is Ferrari.";
static final String DESCRIPTION = "This is a copper coin.";
@Override
public String getDescription() {
@ -62,51 +63,48 @@ public class Ferrari implements Car {
}
```
Enumeration above represents types of cars that we support (`Ford` and `Ferrari`).
Enumeration above represents types of coins that we support (`GoldCoin` and `CopperCoin`).
```java
public enum CarType {
FORD(Ford::new),
FERRARI(Ferrari::new);
private final Supplier<Car> constructor;
CarType(Supplier<Car> constructor) {
this.constructor = constructor;
}
public Supplier<Car> getConstructor() {
return this.constructor;
}
@RequiredArgsConstructor
@Getter
public enum CoinType {
COPPER(CopperCoin::new),
GOLD(GoldCoin::new);
private final Supplier<Coin> constructor;
}
```
Then we have the static method `getCar` to create car objects encapsulated in the factory class
`CarsFactory`.
Then we have the static method `getCoin` to create coin objects encapsulated in the factory class
`CoinFactory`.
```java
public class CarsFactory {
public static Car getCar(CarType type) {
public class CoinFactory {
public static Coin getCoin(CoinType type) {
return type.getConstructor().get();
}
}
```
Now on the client code we can create different types of cars using the factory class.
Now on the client code we can create different types of coins 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());
LOGGER.info("The alchemist begins his work.");
var coin1 = CoinFactory.getCoin(CoinType.COPPER);
var coin2 = CoinFactory.getCoin(CoinType.GOLD);
LOGGER.info(coin1.getDescription());
LOGGER.info(coin2.getDescription());
```
Program output:
```java
This is Ford.
This is Ferrari.
The alchemist begins his work.
This is a copper coin.
This is a gold coin.
```
## Class Diagram
@ -115,7 +113,7 @@ This is Ferrari.
## Applicability
Use the Simple Factory pattern when you only care about the creation of a object, not how to create
Use the factory pattern when you only care about the creation of a object, not how to create
and manage it.
Pros
@ -127,13 +125,13 @@ Cons
* The code becomes more complicated than it should be.
## Real world examples
## Known uses
* [java.util.Calendar#getInstance()](https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--)
* [java.util.ResourceBundle#getBundle()](https://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html#getBundle-java.lang.String-)
* [java.text.NumberFormat#getInstance()](https://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html#getInstance--)
* [java.nio.charset.Charset#forName()](https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-)
* [java.net.URLStreamHandlerFactory#createURLStreamHandler(String)](https://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html) (Returns different singleton objects, depending on a protocol)
* [java.net.URLStreamHandlerFactory#createURLStreamHandler(String)](https://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html) (returns different singleton objects, depending on a protocol)
* [java.util.EnumSet#of()](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of(E))
* [javax.xml.bind.JAXBContext#createMarshaller()](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--) and other similar methods.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -5,31 +5,32 @@ package com.iluwatar.factory {
+ App()
+ main(args : String[]) {static}
}
interface Car {
interface Coin {
+ getDescription() : String {abstract}
}
class CarsFactory {
+ CarsFactory()
+ getCar(type : CarType) : Car {static}
class CoinFactory {
+ CoinFactory()
+ getCoin(type : CoinType) : Coin {static}
}
~enum CarType {
+ FERRARI {static}
+ FORD {static}
+ valueOf(name : String) : CarType {static}
+ values() : CarType[] {static}
enum CoinType {
+ COPPER {static}
+ GOLD {static}
- constructor : Supplier<Coin>
+ getConstructor() : Supplier<Coin>
+ valueOf(name : String) : CoinType {static}
+ values() : CoinType[] {static}
}
class Ferrari {
class CopperCoin {
~ DESCRIPTION : String {static}
+ Ferrari()
+ CopperCoin()
+ getDescription() : String
}
class Ford {
class GoldCoin {
~ DESCRIPTION : String {static}
+ Ford()
+ GoldCoin()
+ getDescription() : String
}
}
CarType ..+ CarsFactory
Ferrari ..|> Car
Ford ..|> Car
CopperCoin ..|> Coin
GoldCoin ..|> Coin
@enduml

View File

@ -30,8 +30,8 @@ import lombok.extern.slf4j.Slf4j;
* 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.
*
* <p>In this example the CarFactory is the factory class and it provides a static method to
* create different cars.
* <p>In this example an alchemist manufactures coins. CoinFactory is the factory class and it
* provides a static method to create different types of coins.
*/
@Slf4j
@ -41,9 +41,10 @@ public class App {
* 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());
LOGGER.info("The alchemist begins his work.");
var coin1 = CoinFactory.getCoin(CoinType.COPPER);
var coin2 = CoinFactory.getCoin(CoinType.GOLD);
LOGGER.info(coin1.getDescription());
LOGGER.info(coin2.getDescription());
}
}

View File

@ -24,9 +24,9 @@
package com.iluwatar.factory;
/**
* Car interface.
* Coin interface.
*/
public interface Car {
public interface Coin {
String getDescription();

View File

@ -24,14 +24,14 @@
package com.iluwatar.factory;
/**
* Factory of cars.
* Factory of coins.
*/
public class CarsFactory {
public class CoinFactory {
/**
* Factory method takes as parameter a car type and initiate the appropriate class.
* Factory method takes as a parameter the coin type and calls the appropriate class.
*/
public static Car getCar(CarType type) {
public static Coin getCoin(CoinType type) {
return type.getConstructor().get();
}
}

View File

@ -28,15 +28,14 @@ import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* Enumeration for different types of cars.
* Enumeration for different types of coins.
*/
@RequiredArgsConstructor
@Getter
public enum CarType {
public enum CoinType {
FORD(Ford::new),
FERRARI(Ferrari::new);
private final Supplier<Car> constructor;
COPPER(CopperCoin::new),
GOLD(GoldCoin::new);
private final Supplier<Coin> constructor;
}

View File

@ -24,11 +24,11 @@
package com.iluwatar.factory;
/**
* Ferrari implementation.
* CopperCoin implementation.
*/
public class Ferrari implements Car {
public class CopperCoin implements Coin {
static final String DESCRIPTION = "This is Ferrari.";
static final String DESCRIPTION = "This is a copper coin.";
@Override
public String getDescription() {

View File

@ -24,11 +24,11 @@
package com.iluwatar.factory;
/**
* Ford implementation.
* GoldCoin implementation.
*/
public class Ford implements Car {
public class GoldCoin implements Coin {
static final String DESCRIPTION = "This is Ford.";
static final String DESCRIPTION = "This is a gold coin.";
@Override
public String getDescription() {

View File

@ -27,12 +27,11 @@ import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class CarsFactoryTest {
class CoinFactoryTest {
@Test
void shouldReturnFerrariInstance() {
final var ferrari = CarsFactory.getCar(CarType.FERRARI);
assertTrue(ferrari instanceof Ferrari);
void shouldReturnGoldCoinInstance() {
final var goldCoin = CoinFactory.getCoin(CoinType.GOLD);
assertTrue(goldCoin instanceof GoldCoin);
}
}