diff --git a/currying/README.md b/currying/README.md new file mode 100644 index 000000000000..c526220eabb1 --- /dev/null +++ b/currying/README.md @@ -0,0 +1,128 @@ +--- +layout: pattern title: Currying folder: currying permalink: /patterns/currying/ categories: Functional language: en +tags: + +- Decoupling + +--- + +## Intent + +Currying pattern transforms an arbitrary arity into a sequence of unary functions. + +## Explanation + +Real-world example + +> We have a Staff class for storing employees' information, e.g. firstName, lastName, +> gender, email, etc. We want to adapt currying pattern to simplify the parameters handling, +> we don't need to provide all parameters at the same time. We can provide them one by one +> when we have that data. + +In plain words + +> It helps you to avoid passing the same variable again and again. +> It helps to create a higher order function. It extremely helpful in event handling. +> Little snippets of code can be written and reused with ease. + +Wikipedia says + +> Currying provides a way for working with functions that take multiple arguments, +> and using them in frameworks where functions might take only one argument. +> For example, some analytical techniques can only be applied to functions with a +> single argument. Practical functions frequently take more arguments than this. +> Frege showed that it was sufficient to provide solutions for the single argument +> case, as it was possible to transform a function with multiple arguments into a +> chain of single-argument functions instead. This transformation is the process +> now known as currying. + +**Programmatic Example** + +First, we have the `Staff` class: + +```java + +@AllArgsConstructor +@Data +public class Staff { + private String firstName; + private String lastName; + private Gender gender; + private String email; + private LocalDate dateOfBirth; +} +``` + +By using @AllArgsConstructor from Lombok library, it will generate a constructor with all attributes. All paramaters +need to provide at the same time. + +Next, we will try to use Curry pattern to transforms an arbitrary arity into a sequence of unary functions. + +```java +static Function>>>>CREATOR= + firstName->lastName + ->gender->email + ->dateOfBirth + ->new Staff(firstName,lastName,gender, + email,dateOfBirth); +``` + +With using this way, we can create Staff object by providing parameters one by one: + +```java +Staff.CREATOR + .apply(firstName) + .apply(lastName) + .apply(gender) + .apply(email) + .apply(dateOfBirth); +``` + +We can also use Functional Interface to implement currying function. + +```java +static AddFirstName builder(){ + return firstName->lastName + ->gender->email + ->dateOfBirth + ->new Staff(firstName,lastName,gender,email,dateOfBirth); + } + +interface AddFirstName { + AddLastName withReturnFirstName(String firstName); +} + +interface AddLastName { + AddGender withReturnLastName(String lastName); +} + +interface AddGender { + AddEmail withReturnGender(Gender gender); +} + +interface AddEmail { + AddDateOfBirth withReturnEmail(String email); +} + +interface AddDateOfBirth { + Staff withReturnDateOfBirth(LocalDate dateOfBirth); +} +``` + +## Applicability + +Use the currying pattern in any of the following situations + +* when you want to break a function with many arguments into many functions with single argument + +## Tutorials + +* [Currying in Java](https://www.baeldung.com/java-currying) + +## Credits + +* [Baeldung](https://www.baeldung.com/) \ No newline at end of file diff --git a/currying/pom.xml b/currying/pom.xml new file mode 100644 index 000000000000..14ea821bb78b --- /dev/null +++ b/currying/pom.xml @@ -0,0 +1,62 @@ + + + + + java-design-patterns + com.iluwatar + 1.25.0-SNAPSHOT + + 4.0.0 + currying + + + junit + junit + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.dao.App + + + + + + + + + diff --git a/currying/src/main/java/com/iluwatar/currying/Staff.java b/currying/src/main/java/com/iluwatar/currying/Staff.java new file mode 100644 index 000000000000..762e0159d550 --- /dev/null +++ b/currying/src/main/java/com/iluwatar/currying/Staff.java @@ -0,0 +1,97 @@ +package com.iluwatar.currying; + +import java.time.LocalDate; +import java.util.function.Function; +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * Staff Object for demonstrating how to use currying pattern. + */ +@AllArgsConstructor +@Data +public class Staff { + private String firstName; + private String lastName; + private Gender gender; + private String email; + private LocalDate dateOfBirth; + + /** + * Use {@link Function} for currying. + */ + static Function>>>> CREATOR = + firstName -> lastName + -> gender -> email + -> dateOfBirth + -> new Staff(firstName, lastName, gender, + email, dateOfBirth); + + /** + * Use functional interfaces for currying. + */ + static AddFirstName builder() { + return firstName -> lastName + -> gender -> email + -> dateOfBirth + -> new Staff(firstName, lastName, gender, email, dateOfBirth); + } + + interface AddFirstName { + AddLastName withReturnFirstName(String firstName); + } + + interface AddLastName { + AddGender withReturnLastName(String lastName); + } + + interface AddGender { + AddEmail withReturnGender(Gender gender); + } + + interface AddEmail { + AddDateOfBirth withReturnEmail(String email); + } + + interface AddDateOfBirth { + Staff withReturnDateOfBirth(LocalDate dateOfBirth); + } + + enum Gender { + Male, Female + } + + /** + * Main method for maven-assembly-plugin. + */ + public static void main(String[] args) { + final String firstName = "Janus"; + final String lastName = "Lin"; + final Staff.Gender gender = Staff.Gender.Male; + final String email = "example@gmail.com"; + final LocalDate dateOfBirth = LocalDate.now(); + + Staff staff1 = Staff.CREATOR + .apply(firstName) + .apply(lastName) + .apply(gender) + .apply(email) + .apply(dateOfBirth); + + System.out.println(String.format("Staff created with basic currying: %s", staff1)); + + Staff staff2 = Staff.builder() + .withReturnFirstName(firstName) + .withReturnLastName(lastName) + .withReturnGender(gender) + .withReturnEmail(email) + .withReturnDateOfBirth(dateOfBirth); + + System.out.println( + String.format("Staff created with currying and functional interfaces: %s", staff2)); + } +} diff --git a/currying/src/test/java/com/iluwatar/currying/StaffTest.java b/currying/src/test/java/com/iluwatar/currying/StaffTest.java new file mode 100644 index 000000000000..f74dfb532bd8 --- /dev/null +++ b/currying/src/test/java/com/iluwatar/currying/StaffTest.java @@ -0,0 +1,58 @@ +package com.iluwatar.currying; + +import org.junit.Assert; +import org.junit.Test; + +import java.time.LocalDate; + +import static org.junit.Assert.*; + +public class StaffTest { + private final String firstName = "Janus"; + private final String lastName = "Lin"; + private final Staff.Gender gender = Staff.Gender.Male; + private final String email = "example@gmail.com"; + private final LocalDate dateOfBirth = LocalDate.now(); + + private final Staff expectedResult = new Staff(firstName, lastName, gender, email, dateOfBirth); + + @Test + public void createStaffWithBasicCurrying() { + Staff actualResult = Staff.CREATOR + .apply(firstName) + .apply(lastName) + .apply(gender) + .apply(email) + .apply(dateOfBirth); + assertEquals(expectedResult, actualResult); + } + + @Test + public void createStaffWithFunctionalInterface() { + Staff actualResult = Staff.builder() + .withReturnFirstName(firstName) + .withReturnLastName(lastName) + .withReturnGender(gender) + .withReturnEmail(email) + .withReturnDateOfBirth(dateOfBirth); + assertEquals(expectedResult, actualResult); + } + + @Test + public void mainTest() { + Staff.main(new String[]{}); + } + + @Test + public void hashTest() { + expectedResult.hashCode(); + } + + @Test + public void equalTest() { + assertTrue(expectedResult.equals(expectedResult)); + assertFalse(expectedResult.equals(new Integer(1))); + Staff o2 = new Staff(firstName, lastName, gender, email, dateOfBirth); + assertTrue(expectedResult.equals(o2)); + } +} diff --git a/pom.xml b/pom.xml index a724dc441b6e..ec3760dc3482 100644 --- a/pom.xml +++ b/pom.xml @@ -27,6 +27,553 @@ ZK framework is licensed under LGPL and the license can be found at lgpl-3.0.txt --> + + 4.0.0 + com.iluwatar + java-design-patterns + 1.25.0-SNAPSHOT + pom + 2014-2021 + + UTF-8 + 3.8.0.2131 + 5.2.18.Final + 5.0.17.RELEASE + 2.0.9.RELEASE + 2.0.14.RELEASE + 1.4.190 + 4.12 + 5.7.1 + ${junit-jupiter.version} + 3.8.1 + 0.8.6 + 1.4 + 2.24.0 + 19.0 + 3.5.6 + 2.22 + 4.0 + 3.12.1 + 1.7.30 + 1.2.3 + 1.1.0 + 1.12.13 + 2.0.1 + 2.12.3 + 2.3.1 + 2.3.2 + 1.3.2 + 1.1.0 + 2.0.0 + 3.5.0 + 1.18.20 + 1.11.5 + 3.27.0-GA + 3.0.0-M5 + 3.1.0 + 0.3.1 + 3.0 + 1.4.8 + 2.7 + + https://sonarcloud.io + iluwatar + iluwatar_java-design-patterns + ${project.artifactId} + Java Design Patterns + + + abstract-factory + tls + builder + factory-method + prototype + singleton + adapter + bridge + composite + dao + data-mapper + decorator + facade + flyweight + proxy + chain-of-responsibility + command + interpreter + iterator + mediator + memento + model-view-presenter + observer + state + strategy + template-method + version-number + visitor + double-checked-locking + servant + service-locator + null-object + event-aggregator + callback + execute-around + property + intercepting-filter + producer-consumer + pipeline + poison-pill + reader-writer-lock + lazy-loading + service-layer + specification + tolerant-reader + model-view-controller + flux + double-dispatch + multiton + resource-acquisition-is-initialization + thread-pool + twin + private-class-data + object-pool + dependency-injection + naked-objects + front-controller + repository + async-method-invocation + monostate + step-builder + business-delegate + half-sync-half-async + layers + eip-message-channel + fluentinterface + reactor + caching + eip-publish-subscribe + delegation + event-driven-architecture + api-gateway + factory-kit + feature-toggle + value-object + module + monad + mute-idiom + hexagonal + abstract-document + aggregator-microservices + promise + page-object + event-asynchronous + event-queue + queue-load-leveling + object-mother + data-bus + converter + guarded-suspension + balking + extension-objects + marker + cqrs + event-sourcing + data-transfer-object + throttling + unit-of-work + partial-response + eip-wire-tap + eip-splitter + eip-aggregator + retry + dirty-flag + trampoline + serverless + ambassador + acyclic-visitor + collection-pipeline + master-worker-pattern + spatial-partition + priority-queue + commander + typeobjectpattern + bytecode + leader-election + data-locality + subclass-sandbox + circuit-breaker + role-object + saga + double-buffer + sharding + game-loop + combinator + update-method + leader-followers + strangler + arrange-act-assert + transaction-script + registry + filterer + factory + separated-interface + special-case + parameter-object + active-object + model-view-viewmodel + composite-entity + table-module + presentation + lockable-object + fanout-fanin + domain-model + + + + jitpack.io + https://jitpack.io + + + + + + net.bytebuddy + byte-buddy + ${byte-buddy.version} + test + + + net.bytebuddy + byte-buddy-agent + ${byte-buddy.version} + test + + + org.hibernate + hibernate-core + ${hibernate.version} + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + org.springframework.data + spring-data-jpa + ${spring-data.version} + + + org.springframework + spring-webmvc + ${spring.version} + + + com.h2database + h2 + ${h2.version} + + + commons-dbcp + commons-dbcp + ${commons-dbcp.version} + + + org.apache.camel + camel-core + ${camel.version} + + + org.apache.camel + camel-stream + ${camel.version} + + + org.junit.jupiter + junit-jupiter-api + ${junit-jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit-jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit-jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-migrationsupport + ${junit-jupiter.version} + test + + + org.junit.vintage + junit-vintage-engine + ${junit-vintage.version} + test + + + org.mockito + mockito-core + ${mockito.version} + test + + + com.google.guava + guava + ${guava.version} + + + net.sourceforge.htmlunit + htmlunit + ${htmlunit.version} + + + com.google.inject + guice + ${guice.version} + + + org.mongodb + mongo-java-driver + ${mongo-java-driver.version} + + + javax.xml.bind + jaxb-api + ${jaxb-api.version} + + + javax.annotation + javax.annotation-api + ${annotation-api.version} + + + com.sun.xml.bind + jaxb-impl + ${jaxb-impl.version} + + + org.javassist + javassist + ${javassist.version} + + + com.github.stefanbirkner + system-lambda + ${system-lambda.version} + test + + + commons-io + commons-io + ${commons-io.version} + + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + ch.qos.logback + logback-core + ${logback.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${compiler.version} + + 11 + 11 + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + jar-with-dependencies + + + ${project.artifactId} + false + + + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + ${sonar-maven-plugin.version} + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven-checkstyle-plugin.version} + + + validate + + check + + validate + + google_checks.xml + checkstyle-suppressions.xml + UTF-8 + true + warning + false + + + + + + + org.commonjava.maven.plugins + directory-maven-plugin + ${directory-maven-plugin.version} + + + directories + + directory-of + + initialize + + projectRoot + + com.iluwatar + java-design-patterns + + + + + + + com.mycila + license-maven-plugin + ${license-maven-plugin.version} + +
com/mycila/maven/plugin/license/templates/MIT.txt
+ + Ilkka Seppälä + + true + + license-plugin-header-style.xml + + + SLASHSTAR_CUSTOM_STYLE + + + .github/FUNDING.yml + +
+ + + install-format + install + + format + + + +
+ + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + prepare-agent + + prepare-agent + + + + report + + report + + + + + + com.iluwatar.urm + urm-maven-plugin + ${urm-maven-plugin.version} + + + ${project.basedir}/etc + + com.iluwatar + + true + false + plantuml + + + + process-classes + + map + + + + +
+
4.0.0 com.iluwatar @@ -227,6 +774,7 @@ lockable-object fanout-fanin domain-model + currying @@ -439,139 +987,4 @@ jar-with-dependencies - ${project.artifactId} - false - - - - - - org.sonarsource.scanner.maven - sonar-maven-plugin - ${sonar-maven-plugin.version} - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - ${maven-checkstyle-plugin.version} - - - validate - - check - - validate - - google_checks.xml - checkstyle-suppressions.xml - UTF-8 - true - warning - false - - - - - - - org.commonjava.maven.plugins - directory-maven-plugin - ${directory-maven-plugin.version} - - - directories - - directory-of - - initialize - - projectRoot - - com.iluwatar - java-design-patterns - - - - - - - com.mycila - license-maven-plugin - ${license-maven-plugin.version} - -
com/mycila/maven/plugin/license/templates/MIT.txt
- - Ilkka Seppälä - - true - - license-plugin-header-style.xml - - - SLASHSTAR_CUSTOM_STYLE - - - .github/FUNDING.yml - -
- - - install-format - install - - format - - - -
- - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - prepare-agent - - prepare-agent - - - - report - - report - - - - - - com.iluwatar.urm - urm-maven-plugin - ${urm-maven-plugin.version} - - - ${project.basedir}/etc - - com.iluwatar - - true - false - plantuml - - - - process-classes - - map - - - - -
-