mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-14 08:58:26 +00:00
* #1842 Setting up project and creating example classes. Issues running site and deploy * #1842 Added unit tests * #1842 Improved example * #1842 Added UML class diagram * #1842 Added comments to Genre class * #1842 Improved readability of lambda function * #1842 Started working on the README and created initial UML * #1842 Added example to README * #1842 Replaced prints with LOGGER * #1842 Fixed typo in README * #1842 Testing commit account * #1842 Adding documentation to App class * #1842 Improved documentation * #1842 Added documentation to AppTest * #1842 Fixing latex formating issue * #1842 Improving the intent description * #1842 Removed override methods from the UML diagram for clarity * #1842 Renamed the SCI_FI enum * #1842 Updated the currying pom.xml * #1842 Removed unneeded comment * #1842 Improving documentation and README * Added review changes. * Fixing build issues and added javadoc comments to functional interfaces. * Removing code smells * Removed unnecessary toString method * Using lombok to reduce boiler plate. * Fixed frontmatter. * Removing function name code smell * Fixed README typo * Added book_creator test to improve coverage Co-authored-by: Hugo Kat <u7286091@anu.edu.au>
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
---
|
||||
title: Currying
|
||||
category: Functional
|
||||
language: en
|
||||
tag:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## Name / classification
|
||||
Currying
|
||||
|
||||
## Intent
|
||||
Currying decomposes a function that takes multiple arguments into a sequence of functions that each take a single argument.
|
||||
Curried functions are useful since they can be used to create new functions with lower arity to perform more specialised tasks
|
||||
in a concise and readable manner. This is done via partial application.
|
||||
|
||||
## Explanation
|
||||
Real-world example
|
||||
> Consider a librarian who wants to populate their library with books. The librarian wants functions which can create
|
||||
> books corresponding to specific genres and authors. Currying makes this possible by writing a curried book builder
|
||||
> function and utilising partial application.
|
||||
|
||||
In plain words
|
||||
> Decompose a function that take multiple arguments into multiple functions that take a single argument.
|
||||
|
||||
Wikipedia says
|
||||
> Currying is the technique of converting a function that takes multiple arguments into a sequence of functions that
|
||||
> each take a single argument. Given a function $f:(X \times Y) \rightarrow Z$, currying constructs a new function
|
||||
> $h:X \rightarrow (Y\rightarrow Z)$. $h$ takes an argument from $X$ and returns a function which maps $Y$ to $Z$. Hence,
|
||||
> $h(x)(y) = f(x, y)$.
|
||||
|
||||
Programmatic example
|
||||
We have a `Book` class and `Genre` enum.
|
||||
```java
|
||||
public class Book {
|
||||
private final Genre genre;
|
||||
private final String author;
|
||||
private final String title;
|
||||
private final LocalDate publicationDate;
|
||||
|
||||
Book(Genre genre, String author, String title, LocalDate publicationDate) {
|
||||
this.genre = genre;
|
||||
this.author = author;
|
||||
this.title = title;
|
||||
this.publicationDate = publicationDate;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Genre {
|
||||
FANTASY,
|
||||
HORROR,
|
||||
SCI_FI;
|
||||
}
|
||||
```
|
||||
We could easily create a `Book` object with the following method:
|
||||
```java
|
||||
Book createBook(Genre genre, String author, String title, LocalDate publicationDate) {
|
||||
return new Book(genre, author, title, publicationDate);
|
||||
}
|
||||
```
|
||||
However, what if we only wanted to create books from the `FANTASY` genre? We could pass in the `FANTASY` parameter on each method call; however, this is repetitive. We could define a new method specifically for creating `FANTASY` books; however, it is infeasible to create a new method for each book genre. The solution is to create a curried function.
|
||||
```java
|
||||
static Function<Genre, Function<String, Function<String, Function<LocalDate, Book>>>> book_creator
|
||||
= genre
|
||||
-> author
|
||||
-> title
|
||||
-> publicationDate
|
||||
-> new Book(genre, author, title, publicationDate);
|
||||
```
|
||||
Note that the order of the parameters is important. `genre` must come before `author`, `author` must come before `title` and so on. We must be considerate of this when writing curried functions to take full advantage of partial application. Using the above function, we can define a new function `fantasyBookFunc`, to generate `FANTASY` books as follows:
|
||||
```java
|
||||
Function<String, Function<String, Function<LocalDate, Book>>> fantasyBookFunc = Book.book_creator.apply(Genre.FANTASY);
|
||||
```
|
||||
Unfortunately, the type signature of `BOOK_CREATOR` and `fantasyBookFunc` are difficult to read and understand. We can improve this by using the [builder pattern](https://java-design-patterns.com/patterns/builder/) and [functional interfaces](https://www.geeksforgeeks.org/functional-interfaces-java/#:~:text=A%20functional%20interface%20is%20an,any%20number%20of%20default%20methods).
|
||||
```java
|
||||
public static AddGenre builder() {
|
||||
return genre
|
||||
-> author
|
||||
-> title
|
||||
-> publicationDate
|
||||
-> new Book(genre, author, title, publicationDate);
|
||||
}
|
||||
|
||||
public interface AddGenre {
|
||||
Book.AddAuthor withGenre(Genre genre);
|
||||
}
|
||||
|
||||
public interface AddAuthor {
|
||||
Book.AddTitle withAuthor(String author);
|
||||
}
|
||||
|
||||
public interface AddTitle {
|
||||
Book.AddPublicationDate withTitle(String title);
|
||||
}
|
||||
|
||||
public interface AddPublicationDate {
|
||||
Book withPublicationDate(LocalDate publicationDate);
|
||||
}
|
||||
```
|
||||
The semantics of the `builder` function can easily be understood. The `builder` function returns a function `AddGenre`, which adds the genre to the book. Similarity, the `AddGenre` function returns another function `AddTitle`, which adds the title to the book and so on, until the `AddPublicationDate` function returns a `Book`.
|
||||
For example, we could create a `Book` as follows:
|
||||
```java
|
||||
Book book = Book.builder().withGenre(Genre.FANTAST)
|
||||
.withAuthor("Author")
|
||||
.withTitle("Title")
|
||||
.withPublicationDate(LocalDate.of(2000, 7, 2));
|
||||
```
|
||||
The below example demonstrates how partial application can be used with the `builder` function to create specialised book builder functions.
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
LOGGER.info("Librarian begins their work.");
|
||||
|
||||
// Defining genre book functions
|
||||
Book.AddAuthor fantasyBookFunc = Book.builder().withGenre(Genre.FANTASY);
|
||||
Book.AddAuthor horrorBookFunc = Book.builder().withGenre(Genre.HORROR);
|
||||
Book.AddAuthor scifiBookFunc = Book.builder().withGenre(Genre.SCI_FI);
|
||||
|
||||
// Defining author book functions
|
||||
Book.AddTitle kingFantasyBooksFunc = fantasyBookFunc.withAuthor("Stephen King");
|
||||
Book.AddTitle kingHorrorBooksFunc = horrorBookFunc.withAuthor("Stephen King");
|
||||
Book.AddTitle rowlingFantasyBooksFunc = fantasyBookFunc.withAuthor("J.K. Rowling");
|
||||
|
||||
// Creates books by Stephen King (horror and fantasy genres)
|
||||
Book shining = kingHorrorBooksFunc.withTitle("The Shining")
|
||||
.withPublicationDate(LocalDate.of(1977, 1, 28));
|
||||
Book darkTower = kingFantasyBooksFunc.withTitle("The Dark Tower: Gunslinger")
|
||||
.withPublicationDate(LocalDate.of(1982, 6, 10));
|
||||
|
||||
// Creates fantasy books by J.K. Rowling
|
||||
Book chamberOfSecrets = rowlingFantasyBooksFunc.withTitle("Harry Potter and the Chamber of Secrets")
|
||||
.withPublicationDate(LocalDate.of(1998, 7, 2));
|
||||
|
||||
// Create sci-fi books
|
||||
Book dune = scifiBookFunc.withAuthor("Frank Herbert")
|
||||
.withTitle("Dune")
|
||||
.withPublicationDate(LocalDate.of(1965, 8, 1));
|
||||
Book foundation = scifiBookFunc.withAuthor("Isaac Asimov")
|
||||
.withTitle("Foundation")
|
||||
.withPublicationDate(LocalDate.of(1942, 5, 1));
|
||||
|
||||
LOGGER.info("Stephen King Books:");
|
||||
LOGGER.info(shining.toString());
|
||||
LOGGER.info(darkTower.toString());
|
||||
|
||||
LOGGER.info("J.K. Rowling Books:");
|
||||
LOGGER.info(chamberOfSecrets.toString());
|
||||
|
||||
LOGGER.info("Sci-fi Books:");
|
||||
LOGGER.info(dune.toString());
|
||||
LOGGER.info(foundation.toString());
|
||||
}
|
||||
```
|
||||
Program output:
|
||||
```
|
||||
Librarian begins their work.
|
||||
Stephen King Books:
|
||||
Book{genre=HORROR, author='Stephen King', title='The Shining', publicationDate=1977-01-28}
|
||||
Book{genre=FANTASY, author='Stephen King', title='The Dark Tower: Gunslinger', publicationDate=1982-06-10}
|
||||
J.K. Rowling Books:
|
||||
Book{genre=FANTASY, author='J.K. Rowling', title='Harry Potter and the Chamber of Secrets', publicationDate=1998-07-02}
|
||||
Sci-fi Books:
|
||||
Book{genre=SCI_FI, author='Frank Herbert', title='Dune', publicationDate=1965-08-01}
|
||||
Book{genre=SCI_FI, author='Isaac Asimov', title='Foundation', publicationDate=1942-05-01}
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
||||
## Applicability
|
||||
A curried function which has only been passed some of its arguments is called a partial application. Partial application
|
||||
allows for the creation of functions with some pre-defined data in their scope, since partial application can be used to
|
||||
create specialised functions with lower arity. This abstraction can help keep code readable and concise. Therefore, currying is useful when frequently calling functions with fixed parameters.
|
||||
|
||||
## Known uses
|
||||
Most functional programming languages support curried functions. A popular example is [Haskell](https://www.haskell.org/), in which all functions are considered curried.
|
||||
|
||||
## Consequences
|
||||
Pros
|
||||
* Currying allows for partial application, which can be used to create specialised functions concisely.
|
||||
|
||||
Cons
|
||||
* The order of the parameters in a curried function is important since we want to take advantage of partial application. It is best to input the most general parameters first and input specific parameters last.
|
||||
* As shown in the programmatic example above, curried functions with several parameters have a cumbersome type signature (in Java).
|
||||
|
||||
## Related patterns
|
||||
* [Builder patter](https://java-design-patterns.com/patterns/builder/)
|
||||
|
||||
## Credits
|
||||
* [Currying in Java](https://www.baeldung.com/java-currying)
|
||||
* [What Is Currying in Programming](https://towardsdatascience.com/what-is-currying-in-programming-56fd57103431#:~:text=Currying%20is%20helpful%20when%20you,concise%2C%20and%20more%20readable%20solution.)
|
||||
* [Why the fudge should I use currying?](https://medium.com/dailyjs/why-the-fudge-should-i-use-currying-84e4000c8743)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
@@ -0,0 +1,39 @@
|
||||
@startuml
|
||||
package com.iluwatar.currying {
|
||||
class App {
|
||||
+ App()
|
||||
+ main(args : String[]) {static}
|
||||
}
|
||||
class Book {
|
||||
~ BOOK_CREATOR : Function<Genre, Function<String, Function<String, Function<LocalDate, Book>>>> {static}
|
||||
- author : String
|
||||
- genre : Genre
|
||||
- publicationDate : LocalDate
|
||||
- title : String
|
||||
~ Book(genre : Genre, author : String, title : String, publicationDate : LocalDate)
|
||||
+ builder() : AddGenre {static}
|
||||
}
|
||||
interface AddAuthor {
|
||||
+ withAuthor(String) : AddTitle {abstract}
|
||||
}
|
||||
interface AddGenre {
|
||||
+ withGenre(Genre) : AddAuthor {abstract}
|
||||
}
|
||||
interface AddPublicationDate {
|
||||
+ withPublicationDate(LocalDate) : Book {abstract}
|
||||
}
|
||||
interface AddTitle {
|
||||
+ withTitle(String) : AddPublicationDate {abstract}
|
||||
}
|
||||
enum Genre {
|
||||
+ FANTASY {static}
|
||||
+ HORROR {static}
|
||||
+ SCI_FI {static}
|
||||
}
|
||||
}
|
||||
Book --> "-genre" Genre
|
||||
AddPublicationDate ..+ Book
|
||||
AddAuthor ..+ Book
|
||||
AddTitle ..+ Book
|
||||
AddGenre ..+ Book
|
||||
@enduml
|
||||
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.26.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>currying</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Maven assembly plugin is invoked with default setting which we have
|
||||
in parent pom and specifying the class having main method -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.iluwatar.currying.App</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.currying;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Currying decomposes a function with multiple arguments in multiple functions that
|
||||
* take a single argument. A curried function which has only been passed some of its
|
||||
* arguments is called a partial application. Partial application is useful since it can
|
||||
* be used to create specialised functions in a concise way.
|
||||
*
|
||||
* <p>In this example, a librarian uses a curried book builder function create books belonging to
|
||||
* desired genres and written by specific authors.
|
||||
*/
|
||||
@Slf4j
|
||||
public class App {
|
||||
/**
|
||||
* Main entry point of the program.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
LOGGER.info("Librarian begins their work.");
|
||||
|
||||
// Defining genre book functions
|
||||
Book.AddAuthor fantasyBookFunc = Book.builder().withGenre(Genre.FANTASY);
|
||||
Book.AddAuthor horrorBookFunc = Book.builder().withGenre(Genre.HORROR);
|
||||
Book.AddAuthor scifiBookFunc = Book.builder().withGenre(Genre.SCIFI);
|
||||
|
||||
// Defining author book functions
|
||||
Book.AddTitle kingFantasyBooksFunc = fantasyBookFunc.withAuthor("Stephen King");
|
||||
Book.AddTitle kingHorrorBooksFunc = horrorBookFunc.withAuthor("Stephen King");
|
||||
Book.AddTitle rowlingFantasyBooksFunc = fantasyBookFunc.withAuthor("J.K. Rowling");
|
||||
|
||||
// Creates books by Stephen King (horror and fantasy genres)
|
||||
Book shining = kingHorrorBooksFunc.withTitle("The Shining")
|
||||
.withPublicationDate(LocalDate.of(1977, 1, 28));
|
||||
Book darkTower = kingFantasyBooksFunc.withTitle("The Dark Tower: Gunslinger")
|
||||
.withPublicationDate(LocalDate.of(1982, 6, 10));
|
||||
|
||||
// Creates fantasy books by J.K. Rowling
|
||||
Book chamberOfSecrets = rowlingFantasyBooksFunc.withTitle("Harry Potter and the Chamber of Secrets")
|
||||
.withPublicationDate(LocalDate.of(1998, 7, 2));
|
||||
|
||||
// Create sci-fi books
|
||||
Book dune = scifiBookFunc.withAuthor("Frank Herbert")
|
||||
.withTitle("Dune")
|
||||
.withPublicationDate(LocalDate.of(1965, 8, 1));
|
||||
Book foundation = scifiBookFunc.withAuthor("Isaac Asimov")
|
||||
.withTitle("Foundation")
|
||||
.withPublicationDate(LocalDate.of(1942, 5, 1));
|
||||
|
||||
LOGGER.info("Stephen King Books:");
|
||||
LOGGER.info(shining.toString());
|
||||
LOGGER.info(darkTower.toString());
|
||||
|
||||
LOGGER.info("J.K. Rowling Books:");
|
||||
LOGGER.info(chamberOfSecrets.toString());
|
||||
|
||||
LOGGER.info("Sci-fi Books:");
|
||||
LOGGER.info(dune.toString());
|
||||
LOGGER.info(foundation.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.currying;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* Book class.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class Book {
|
||||
private final Genre genre;
|
||||
private final String author;
|
||||
private final String title;
|
||||
private final LocalDate publicationDate;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Book book = (Book) o;
|
||||
return Objects.equals(author, book.author)
|
||||
&& Objects.equals(genre, book.genre)
|
||||
&& Objects.equals(title, book.title)
|
||||
&& Objects.equals(publicationDate, book.publicationDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(author, genre, title, publicationDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Book{" + "genre=" + genre + ", author='" + author + '\''
|
||||
+ ", title='" + title + '\'' + ", publicationDate=" + publicationDate + '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Curried book builder/creator function.
|
||||
*/
|
||||
static Function<Genre, Function<String, Function<String, Function<LocalDate, Book>>>> book_creator
|
||||
= bookGenre
|
||||
-> bookAuthor
|
||||
-> bookTitle
|
||||
-> bookPublicationDate
|
||||
-> new Book(bookGenre, bookAuthor, bookTitle, bookPublicationDate);
|
||||
|
||||
/**
|
||||
* Implements the builder pattern using functional interfaces to create a more readable book
|
||||
* creator function. This function is equivalent to the BOOK_CREATOR function.
|
||||
*/
|
||||
public static AddGenre builder() {
|
||||
return genre
|
||||
-> author
|
||||
-> title
|
||||
-> publicationDate
|
||||
-> new Book(genre, author, title, publicationDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional interface which adds the genre to a book.
|
||||
*/
|
||||
public interface AddGenre {
|
||||
Book.AddAuthor withGenre(Genre genre);
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional interface which adds the author to a book.
|
||||
*/
|
||||
public interface AddAuthor {
|
||||
Book.AddTitle withAuthor(String author);
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional interface which adds the title to a book.
|
||||
*/
|
||||
public interface AddTitle {
|
||||
Book.AddPublicationDate withTitle(String title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional interface which adds the publication date to a book.
|
||||
*/
|
||||
public interface AddPublicationDate {
|
||||
Book withPublicationDate(LocalDate publicationDate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.currying;
|
||||
|
||||
/**
|
||||
* Enum representing different book genres.
|
||||
*/
|
||||
public enum Genre {
|
||||
FANTASY,
|
||||
HORROR,
|
||||
SCIFI;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.currying;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests that the App can be run without throwing any exceptions.
|
||||
*/
|
||||
class AppTest {
|
||||
@Test
|
||||
void executesWithoutExceptions() {
|
||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.currying;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* Unit tests for the Book class
|
||||
*/
|
||||
class BookCurryingTest {
|
||||
private static Book expectedBook;
|
||||
|
||||
@BeforeAll
|
||||
public static void initialiseBook() {
|
||||
expectedBook = new Book(Genre.FANTASY,
|
||||
"Dave",
|
||||
"Into the Night",
|
||||
LocalDate.of(2002, 4, 7));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the expected book can be created via curried functions
|
||||
*/
|
||||
@Test
|
||||
void createsExpectedBook() {
|
||||
Book builderCurriedBook = Book.builder()
|
||||
.withGenre(Genre.FANTASY)
|
||||
.withAuthor("Dave")
|
||||
.withTitle("Into the Night")
|
||||
.withPublicationDate(LocalDate.of(2002, 4, 7));
|
||||
|
||||
Book funcCurriedBook = Book.book_creator
|
||||
.apply(Genre.FANTASY)
|
||||
.apply("Dave")
|
||||
.apply("Into the Night")
|
||||
.apply(LocalDate.of(2002, 4, 7));
|
||||
|
||||
assertEquals(expectedBook, builderCurriedBook);
|
||||
assertEquals(expectedBook, funcCurriedBook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an intermediate curried function can be used to create the expected book
|
||||
*/
|
||||
@Test
|
||||
void functionCreatesExpectedBook() {
|
||||
Book.AddTitle daveFantasyBookFunc = Book.builder()
|
||||
.withGenre(Genre.FANTASY)
|
||||
.withAuthor("Dave");
|
||||
|
||||
Book curriedBook = daveFantasyBookFunc.withTitle("Into the Night")
|
||||
.withPublicationDate(LocalDate.of(2002, 4, 7));
|
||||
|
||||
assertEquals(expectedBook, curriedBook);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user