feature: #1842 Add Currying Design Pattern (#2271)

* #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:
Hugo Kat
2022-12-04 11:48:57 +02:00
committed by GitHub
co-authored by Hugo Kat
parent 2d309b8928
commit 7be2828c8a
10 changed files with 656 additions and 0 deletions
@@ -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);
}
}