From 66d0b9a6a3a51bf9b206c573df4cfc89a5941fdc Mon Sep 17 00:00:00 2001 From: shahdhoss Date: Sat, 7 Dec 2024 16:04:06 +0200 Subject: [PATCH 1/4] implementation of session facade #1278 --- pom.xml | 1 + session-facade/README.md | 146 ++++++++++++++++++ session-facade/etc/session-facade.urm.png | Bin 0 -> 94854 bytes session-facade/etc/session-facade.urm.puml | 48 ++++++ session-facade/pom.xml | 58 +++++++ .../java/com/iluwatar/sessionfacade/App.java | 49 ++++++ .../iluwatar/sessionfacade/CartService.java | 83 ++++++++++ .../iluwatar/sessionfacade/OrderService.java | 55 +++++++ .../sessionfacade/PaymentService.java | 66 ++++++++ .../com/iluwatar/sessionfacade/Product.java | 40 +++++ .../sessionfacade/ProductCatalogService.java | 44 ++++++ .../sessionfacade/ShoppingFacade.java | 102 ++++++++++++ .../com/iluwatar/sessionfacade/AppTest.java | 42 +++++ .../sessionfacade/CartServiceTest.java | 97 ++++++++++++ .../sessionfacade/PaymentServiceTest.java | 64 ++++++++ .../iluwatar/sessionfacade/ProductTest.java | 77 +++++++++ .../sessionfacade/ShoppingFacadeTest.java | 73 +++++++++ 17 files changed, 1045 insertions(+) create mode 100644 session-facade/README.md create mode 100644 session-facade/etc/session-facade.urm.png create mode 100644 session-facade/etc/session-facade.urm.puml create mode 100644 session-facade/pom.xml create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/App.java create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/CartService.java create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/OrderService.java create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/PaymentService.java create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/Product.java create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/ProductCatalogService.java create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/ShoppingFacade.java create mode 100644 session-facade/src/test/java/com/iluwatar/sessionfacade/AppTest.java create mode 100644 session-facade/src/test/java/com/iluwatar/sessionfacade/CartServiceTest.java create mode 100644 session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java create mode 100644 session-facade/src/test/java/com/iluwatar/sessionfacade/ProductTest.java create mode 100644 session-facade/src/test/java/com/iluwatar/sessionfacade/ShoppingFacadeTest.java diff --git a/pom.xml b/pom.xml index ef3a39265d53..9b5c219ca472 100644 --- a/pom.xml +++ b/pom.xml @@ -218,6 +218,7 @@ function-composition microservices-distributed-tracing microservices-idempotent-consumer + session-facade diff --git a/session-facade/README.md b/session-facade/README.md new file mode 100644 index 000000000000..cb5db50f6f3d --- /dev/null +++ b/session-facade/README.md @@ -0,0 +1,146 @@ +--- +title: "Session Facade Pattern in Java: Simplifying Complex System Interfaces" +shortTitle: Session Facade +description: "Learn how to implement the Session Facade Design Pattern in Java to create a unified interface for complex subsystems. Simplify your code and enhance maintainability with practical examples and use cases." +category: Structural +language: en +tag: + - Abstraction + - API design + - Code simplification + - Decoupling + - Encapsulation + - Gang Of Four + - Interface +--- + +## Also known as + +* Session Facade + +## Intent of Session Facade Design Pattern + +Abstracting the underlying business object interactions by providing a service layer that exposes only the required interfaces + +## Detailed Explanation of Session Facade Pattern with Real-World Examples + +Real-world example + +> In an e-commerce website, users interact with several subsystems like product catalogs, shopping carts, +> payment services, and order management. The Session Facade pattern provides a simplified, centralized interface for these subsystems, +> allowing the client to interact with just a few high-level methods (e.g., addToCart(), placeOrder(), selectPaymentMethod()), instead of directly communicating with each subsystem, using a facade supports low coupling between classes and high cohesion within each service, allowing them to focus on their specific responsibilities. + +In plain words + +> The Session Facade design pattern is an excellent choice for decoupling complex components of the system that need to be interacting frequently. + +## Programmatic Example of Session Facade Pattern in Java + +The Session Facade design pattern is a structural design pattern that provides a simplified interface to a set of complex subsystems, reducing the complexity for the client. This pattern is particularly useful in situations where the client needs to interact with multiple services or systems but doesn’t need to know the internal workings of each service. + +In the context of an e-commerce website, imagine a system where users can browse products, add items to the shopping cart, process payments, and place orders. Instead of the client directly interacting with each individual service (cart, order, payment), the Session Facade provides a single, unified interface for these operations. + +Example Scenario: +In this example, the ShoppingFacade class manages interactions with three subsystems: the `CartService`, `OrderService`, and `PaymentService`. The client interacts with the facade to perform high-level operations like adding items to the cart, placing an order, and selecting a payment method. + +Here’s a simplified programmatic example: +```java +public class App { + public static void main(String[] args) { + ShoppingFacade shoppingFacade = new ShoppingFacade(); + shoppingFacade.addToCart(1); + shoppingFacade.order(); + shoppingFacade.selectPaymentMethod("cash"); + } +} +``` + +The `ShoppingFacade` acts as an intermediary that facilitates interaction between different services promoting low coupling between these services. +```java +public class ShoppingFacade { + List productCatalog; + List cart; + CartService cartService; + OrderService orderService; + PaymentService paymentService; + + public ShoppingFacade() { + productCatalog = new ArrayList<>(); + productCatalog.add(new Product(1, "Wireless Mouse", 25.99, "Ergonomic wireless mouse with USB receiver.")); + productCatalog.add(new Product(2, "Gaming Keyboard", 79.99, "RGB mechanical gaming keyboard with programmable keys.")); + cart = new ArrayList<>(); + cartService = new CartService(cart, productCatalog); + orderService = new OrderService(cart); + paymentService = new PaymentService(); + } + + public void addToCart(int productId) { + this.cartService.addToCart(productId); + } + + public void removeFromCart(int productId) { + this.cartService.removeFromCart(productId); + } + + public void order() { + this.orderService.order(); + } + + public void selectPaymentMethod(String method) { + this.paymentService.selectPaymentMethod(method); + } +} +``` + +Console output for starting the `App` class's `main` method: + +``` +19:43:17.883 [main] INFO com.iluwatar.sessionfacade.CartService -- ID: 1 +Name: Wireless Mouse +Price: $25.99 +Description: Ergonomic wireless mouse with USB receiver. successfully added to the cart +19:43:17.910 [main] INFO com.iluwatar.sessionfacade.OrderService -- Client has chosen to order [ID: 1 +Name: Wireless Mouse +Price: $25.99 +Description: Ergonomic wireless mouse with USB receiver.] +19:43:17.910 [main] INFO com.iluwatar.sessionfacade.PaymentService -- Client have chosen cash payment option +``` + +This is a basic example of the Session Facade design pattern. The actual implementation would depend on specific requirements of your application. + +## When to Use the Session Facade Pattern in Java + +* Use when building complex applications with multiple interacting services, where you want to simplify the interaction between various subsystems. +* Ideal for decoupling complex systems that need to interact but should not be tightly coupled. +* Suitable for applications where you need a single point of entry to interact with multiple backend services, like ecommerce platforms, booking systems, or order management systems. + +## Real-World Applications of Server Session Pattern in Java + +* Enterprise JavaBeans (EJB) +* Java EE (Jakarta EE) Applications + +## Benefits and Trade-offs of Server Session Pattern + + +* Simplifies client-side logic by providing a single entry point for complex operations across multiple services. +* Decouples components of the application, making them easier to maintain, test, and modify without affecting other parts of the system. +* Improves modularity by isolating the implementation details of subsystems from the client. +* Centralizes business logic in one place, making the code easier to manage and update. + +## Trade-offs: + +* Potential performance bottleneck: Since all requests pass through the facade, it can become a bottleneck if not optimized. +* Increased complexity: If the facade becomes too large or complex, it could counteract the modularity it aims to achieve. +* Single point of failure: If the facade encounters issues, it could affect the entire system's operation, making it crucial to handle errors and exceptions properly. + +## Related Java Design Patterns + +* [Facade](https://java-design-patterns.com/patterns/facade/): The Session Facade pattern is a specific application of the more general Facade pattern, which simplifies access to complex subsystems. +* [Command](https://java-design-patterns.com/patterns/command/): Useful for encapsulating requests and passing them to the session facade, which could then manage the execution order. +* [Singleton](https://java-design-patterns.com/patterns/singleton/): Often used to create a single instance of the session facade for managing the entire workflow of a subsystem. + +## References and Credits + +* [Core J2EE Patterns: Best Practices and Design Strategies](https://amzn.to/4cAbDap) +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI) +* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR) diff --git a/session-facade/etc/session-facade.urm.png b/session-facade/etc/session-facade.urm.png new file mode 100644 index 0000000000000000000000000000000000000000..71ab9a0481e021bed6ef39193ccaea8336a71d11 GIT binary patch literal 94854 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%0A_qjOeY)BGXzgT)KGC@tUbbg1eeVICluyKS{#3 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 + - productCatalog : List + + CartService(cart : List, productCatalog : List) + + addToCart(productId : int) + + removeFromCart(productId : int) + } + class OrderService { + - LOGGER : Logger {static} + - cart : List + + OrderService(cart : List) + + order() + } + class PaymentService { + + LOGGER : Logger {static} + + PaymentService() + + cashPayment() + + creditCardPayment() + + selectPaymentMethod(method : String) + } + class ProductCatalogService { + - products : List + + ProductCatalogService(products : List) + } + class ShoppingFacade { + ~ cart : List + ~ cartService : CartService + ~ orderService : OrderService + ~ paymentService : PaymentService + ~ productCatalog : List + + ShoppingFacade() + + addToCart(productId : int) + + order() + + removeFromCart(productId : int) + + selectPaymentMethod(method : String) + } +} +ShoppingFacade --> "-cartService" CartService +ShoppingFacade --> "-paymentService" PaymentService +ShoppingFacade --> "-orderService" OrderService +@enduml \ No newline at end of file diff --git a/session-facade/pom.xml b/session-facade/pom.xml new file mode 100644 index 000000000000..d7e5ad956d41 --- /dev/null +++ b/session-facade/pom.xml @@ -0,0 +1,58 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + + session-facade + + + 17 + 17 + UTF-8 + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.mockito + mockito-core + test + + + + \ No newline at end of file diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/App.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/App.java new file mode 100644 index 000000000000..c5adbf4cbad2 --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/App.java @@ -0,0 +1,49 @@ +/* + * 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.sessionfacade; + +/** + * The type App. + */ +public class App { + /** + * The entry point of application. + * + * @param args the input arguments + */ + public static void main(String[] args) { + ShoppingFacade shoppingFacade1 = new ShoppingFacade(); + shoppingFacade1.addToCart(1); + shoppingFacade1.order(); + shoppingFacade1.selectPaymentMethod("cash"); + + ShoppingFacade shoppingFacade2 = new ShoppingFacade(); + shoppingFacade1.addToCart(2); + shoppingFacade2.order(); + shoppingFacade2.selectPaymentMethod("credit"); + + } +} \ No newline at end of file diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/CartService.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/CartService.java new file mode 100644 index 000000000000..7a091f879e56 --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/CartService.java @@ -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.sessionfacade; + +import java.util.List; +import lombok.extern.slf4j.Slf4j; + + +/** + * The type Cart service. + */ +@Slf4j +public class CartService { + + private final List cart; + private final List productCatalog; + + /** + * Instantiates a new Cart service. + * + * @param cart the cart + * @param productCatalog the product catalog + */ + public CartService(List cart, List productCatalog) { + this.cart = cart; + this.productCatalog = productCatalog; + } + + /** + * Add to cart. + * + * @param productId the product id + */ + public void addToCart(int productId) { + for (Product product : productCatalog) { + if (productId == product.id()) { + this.cart.add(product); + LOGGER.info("{} successfully added to the cart", product); + return; + } + } + LOGGER.info("No product is found with id {}", productId); + } + + /** + * Remove from cart. + * + * @param productId the product id + */ + public void removeFromCart(int productId) { + for (Product product : productCatalog) { + if (productId == product.id()) { + this.cart.remove(product); + LOGGER.info("{} successfully removed from the cart", product); + return; + } + } + LOGGER.info("No product is found with the id {}", productId); + } +} diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/OrderService.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/OrderService.java new file mode 100644 index 000000000000..5a317ab61679 --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/OrderService.java @@ -0,0 +1,55 @@ +/* + * 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.sessionfacade; + +import java.util.List; +import lombok.extern.slf4j.Slf4j; + + +/** + * The type Order service. + */ +@Slf4j +public class OrderService { + private final List cart; + + /** + * Instantiates a new Order service. + * + * @param cart the cart + */ + public OrderService(List cart) { + this.cart = cart; + } + + /** + * Order. + */ + public void order() { + LOGGER.info("Client has chosen to order {}", cart); + cart.clear(); + } +} diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/PaymentService.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/PaymentService.java new file mode 100644 index 000000000000..4d0120e4aa06 --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/PaymentService.java @@ -0,0 +1,66 @@ +/* + * 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.sessionfacade; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The type Payment service. + */ + +public class PaymentService { + public static Logger LOGGER = LoggerFactory.getLogger(PaymentService.class); + + /** + * Select payment method. + * + * @param method the method + */ + public void selectPaymentMethod(String method) { + if (method.equals("cash")) { + cashPayment(); + } else if (method.equals("credit")) { + creditCardPayment(); + } else { + LOGGER.info("Unspecified payment method type"); + } + } + + /** + * Cash payment. + */ + public void cashPayment() { + LOGGER.info("Client have chosen cash payment option"); + } + + /** + * Credit card payment. + */ + public void creditCardPayment() { + LOGGER.info("Client have chosen credit card payment option"); + } +} diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/Product.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/Product.java new file mode 100644 index 000000000000..06e21cd344cb --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/Product.java @@ -0,0 +1,40 @@ +/* + * 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.sessionfacade; + +/** + * The type Product. + */ +public record Product(int id, String name, double price, String description) { + + @Override + public String toString() { + return "ID: " + id + "\nName: " + name + "\nPrice: $" + price + "\nDescription: " + description; + } + +} + + diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/ProductCatalogService.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/ProductCatalogService.java new file mode 100644 index 000000000000..3780a686b52e --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/ProductCatalogService.java @@ -0,0 +1,44 @@ +/* + * 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.sessionfacade; + +import java.util.List; + +/** + * The type Product catalog service. + */ +public class ProductCatalogService { + private List products; + + /** + * Instantiates a new Product catalog service. + * + * @param products the products + */ + public ProductCatalogService(List products) { + this.products = products; + } +} diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/ShoppingFacade.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/ShoppingFacade.java new file mode 100644 index 000000000000..cdc00d98f40c --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/ShoppingFacade.java @@ -0,0 +1,102 @@ +/* + * 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.sessionfacade; + +import java.util.ArrayList; +import java.util.List; + +/** + * The type Shopping facade. + */ +public class ShoppingFacade { + /** + * The Product catalog. + */ + List productCatalog = new ArrayList<>(); + /** + * The Cart. + */ + List cart = new ArrayList<>(); + /** + * The Cart service. + */ + CartService cartService = new CartService(cart, productCatalog); + /** + * The Order service. + */ + OrderService orderService = new OrderService(cart); + /** + * The Payment service. + */ + PaymentService paymentService = new PaymentService(); + + /** + * Instantiates a new Shopping facade. + */ + public ShoppingFacade() { + productCatalog = new ArrayList<>(); + productCatalog.add(new Product(1, "Wireless Mouse", 25.99, "Ergonomic wireless mouse with USB receiver.")); + productCatalog.add(new Product(2, "Gaming Keyboard", 79.99, "RGB mechanical gaming keyboard with programmable keys.")); + cart = new ArrayList<>(); + cartService = new CartService(cart, productCatalog); + orderService = new OrderService(cart); + paymentService = new PaymentService(); + } + + /** + * Add to cart. + * + * @param productId the product id + */ + public void addToCart(int productId) { + this.cartService.addToCart(productId); + } + + /** + * Remove from cart. + * + * @param productId the product id + */ + public void removeFromCart(int productId) { + this.cartService.removeFromCart(productId); + } + + /** + * Order. + */ + public void order() { + this.orderService.order(); + } + + /** + * Select payment method. + * + * @param method the method + */ + public void selectPaymentMethod(String method) { + this.paymentService.selectPaymentMethod(method); + } +} diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/AppTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/AppTest.java new file mode 100644 index 000000000000..012fcf4623ad --- /dev/null +++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/AppTest.java @@ -0,0 +1,42 @@ +/* + * 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.sessionfacade; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +/** + * The type App test. + */ +public class AppTest { + + /** + * Should execute application without exception. + */ + @org.junit.jupiter.api.Test + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); + } + +} diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/CartServiceTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/CartServiceTest.java new file mode 100644 index 000000000000..6f0c48e51f6e --- /dev/null +++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/CartServiceTest.java @@ -0,0 +1,97 @@ +/* + * 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.sessionfacade; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * The type Cart service test. + */ +@Slf4j +class CartServiceTest { + + private CartService cartService; + private List cart; + + /** + * Sets up. + */ + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + cart = new ArrayList<>(); + List productCatalog = new ArrayList<>(); + productCatalog.add(new Product(1, "Product A", 2.0, "any description")); + productCatalog.add(new Product(2, "Product B", 300.0, "a watch")); + cartService = new CartService(cart, productCatalog); + } + + /** + * Test add to cart. + */ + @Test + void testAddToCart() { + cartService.addToCart(1); + assertEquals(1, cart.size()); + assertEquals("Product A", cart.get(0).name()); + } + + /** + * Test remove from cart. + */ + @Test + void testRemoveFromCart() { + cartService.addToCart(1); + assertEquals(1, cart.size()); + cartService.removeFromCart(1); + assertTrue(cart.isEmpty()); + } + + /** + * Test add to cart with invalid product id. + */ + @Test + void testAddToCartWithInvalidProductId() { + cartService.addToCart(999); + assertTrue(cart.isEmpty()); + } + + /** + * Test remove from cart with invalid product id. + */ + @Test + void testRemoveFromCartWithInvalidProductId() { + cartService.removeFromCart(999); + assertTrue(cart.isEmpty()); + } +} diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java new file mode 100644 index 000000000000..fac4b39fbfb8 --- /dev/null +++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java @@ -0,0 +1,64 @@ +/* + * 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.sessionfacade; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; + +import static org.mockito.Mockito.*; + +class PaymentServiceTest { + private PaymentService paymentService; + private Logger mockLogger; + + @BeforeEach + void setUp() { + paymentService = new PaymentService(); + mockLogger = mock(Logger.class); + paymentService.LOGGER = mockLogger; + } + + @Test + void testSelectCashPaymentMethod() { + String method = "cash"; + paymentService.selectPaymentMethod(method); + verify(mockLogger).info("Client have chosen cash payment option"); + } + + @Test + void testSelectCreditCardPaymentMethod() { + String method = "credit"; + paymentService.selectPaymentMethod(method); + verify(mockLogger).info("Client have chosen credit card payment option"); + } + + @Test + void testSelectUnspecifiedPaymentMethod() { + String method = "cheque"; + paymentService.selectPaymentMethod(method); + verify(mockLogger).info("Unspecified payment method type"); + } +} diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/ProductTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/ProductTest.java new file mode 100644 index 000000000000..2d664698551f --- /dev/null +++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/ProductTest.java @@ -0,0 +1,77 @@ +/* + * 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.sessionfacade; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * The type Product test. + */ +public class ProductTest { + /** + * Test product creation. + */ + @Test + public void testProductCreation() { + int id = 1; + String name = "Product A"; + double price = 200.0; + String description = "a description"; + Product product = new Product(id,name,price,description); + assertEquals(id, product.id()); + assertEquals(name, product.name()); + assertEquals(price, product.price()); + assertEquals(description, product.description()); + } + + /** + * Test equals and hash code. + */ + @Test + public void testEqualsAndHashCode() { + Product product1 = new Product(1, "Product A", 99.99, "a description"); + Product product2 = new Product(1, "Product A", 99.99, "a description"); + Product product3 = new Product(2, "Product B", 199.99, "a description"); + + assertEquals(product1, product2); + assertNotEquals(product1, product3); + assertEquals(product1.hashCode(), product2.hashCode()); + assertNotEquals(product1.hashCode(), product3.hashCode()); + } + + /** + * Test to string. + */ + @Test + public void testToString() { + Product product = new Product(1, "Product A", 99.99, "a description"); + String toStringResult = product.toString(); + assertTrue(toStringResult.contains("Product A")); + assertTrue(toStringResult.contains("99.99")); + } + +} diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/ShoppingFacadeTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/ShoppingFacadeTest.java new file mode 100644 index 000000000000..f5fffd49db79 --- /dev/null +++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/ShoppingFacadeTest.java @@ -0,0 +1,73 @@ +/* + * 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.sessionfacade; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for ShoppingFacade. + */ +class ShoppingFacadeTest { + + private ShoppingFacade shoppingFacade; + + @BeforeEach + void setUp() { + shoppingFacade = new ShoppingFacade(); + } + + @Test + void testAddToCart() { + shoppingFacade.addToCart(1); + shoppingFacade.addToCart(2); + List cart = shoppingFacade.cart; + assertEquals(2, cart.size(), "Cart should contain two items."); + assertEquals("Wireless Mouse", cart.get(0).name(), "First item in the cart should be 'Wireless Mouse'."); + assertEquals("Gaming Keyboard", cart.get(1).name(), "Second item in the cart should be 'Gaming Keyboard'."); + } + + @Test + void testRemoveFromCart() { + shoppingFacade.addToCart(1); + shoppingFacade.addToCart(2); + shoppingFacade.removeFromCart(1); + List cart = shoppingFacade.cart; + assertEquals(1, cart.size(), "Cart should contain one item after removal."); + assertEquals("Gaming Keyboard", cart.get(0).name(), "Remaining item should be 'Gaming Keyboard'."); + } + + @Test + void testOrder() { + shoppingFacade.addToCart(1); + shoppingFacade.addToCart(2); + shoppingFacade.order(); + assertTrue(shoppingFacade.cart.isEmpty(), "Cart should be empty after placing the order."); + } +} From aafd33aa2547417bfee116de6d6c7dd59c893f3d Mon Sep 17 00:00:00 2001 From: shahdhoss Date: Sat, 7 Dec 2024 17:01:13 +0200 Subject: [PATCH 2/4] minor change --- .../main/java/com/iluwatar/sessionfacade/App.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/App.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/App.java index c5adbf4cbad2..985648d7c418 100644 --- a/session-facade/src/main/java/com/iluwatar/sessionfacade/App.java +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/App.java @@ -35,15 +35,9 @@ public class App { * @param args the input arguments */ public static void main(String[] args) { - ShoppingFacade shoppingFacade1 = new ShoppingFacade(); - shoppingFacade1.addToCart(1); - shoppingFacade1.order(); - shoppingFacade1.selectPaymentMethod("cash"); - - ShoppingFacade shoppingFacade2 = new ShoppingFacade(); - shoppingFacade1.addToCart(2); - shoppingFacade2.order(); - shoppingFacade2.selectPaymentMethod("credit"); - + ShoppingFacade shoppingFacade = new ShoppingFacade(); + shoppingFacade.addToCart(1); + shoppingFacade.order(); + shoppingFacade.selectPaymentMethod("cash"); } } \ No newline at end of file From a07a7a0a98dc517bafa605624e415595aa0c2186 Mon Sep 17 00:00:00 2001 From: shahdhoss Date: Sat, 7 Dec 2024 17:03:27 +0200 Subject: [PATCH 3/4] readme updated --- session-facade/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/session-facade/README.md b/session-facade/README.md index cb5db50f6f3d..01adda263c62 100644 --- a/session-facade/README.md +++ b/session-facade/README.md @@ -100,10 +100,6 @@ Name: Wireless Mouse Price: $25.99 Description: Ergonomic wireless mouse with USB receiver. successfully added to the cart 19:43:17.910 [main] INFO com.iluwatar.sessionfacade.OrderService -- Client has chosen to order [ID: 1 -Name: Wireless Mouse -Price: $25.99 -Description: Ergonomic wireless mouse with USB receiver.] -19:43:17.910 [main] INFO com.iluwatar.sessionfacade.PaymentService -- Client have chosen cash payment option ``` This is a basic example of the Session Facade design pattern. The actual implementation would depend on specific requirements of your application. From d317ca2a1405ec615f92ae49b6efe98762af9db3 Mon Sep 17 00:00:00 2001 From: shahdhoss Date: Sat, 14 Dec 2024 18:35:09 +0200 Subject: [PATCH 4/4] addressed the comments regarding changing lists to maps and adding maven assembly plugin --- session-facade/README.md | 41 ++++++--- session-facade/pom.xml | 26 ++++-- .../java/com/iluwatar/sessionfacade/App.java | 27 +++++- .../iluwatar/sessionfacade/CartService.java | 44 +++++----- .../iluwatar/sessionfacade/OrderService.java | 42 ++++++++-- .../sessionfacade/PaymentService.java | 13 ++- .../com/iluwatar/sessionfacade/Product.java | 2 - .../sessionfacade/ProductCatalogService.java | 28 +++++-- .../sessionfacade/ShoppingFacade.java | 84 ++++++++++++------- .../sessionfacade/CartServiceTest.java | 14 ++-- .../sessionfacade/PaymentServiceTest.java | 16 ++++ .../sessionfacade/ShoppingFacadeTest.java | 14 ++-- 12 files changed, 248 insertions(+), 103 deletions(-) diff --git a/session-facade/README.md b/session-facade/README.md index 01adda263c62..20a093334e92 100644 --- a/session-facade/README.md +++ b/session-facade/README.md @@ -58,26 +58,30 @@ public class App { The `ShoppingFacade` acts as an intermediary that facilitates interaction between different services promoting low coupling between these services. ```java public class ShoppingFacade { - List productCatalog; - List cart; - CartService cartService; - OrderService orderService; - PaymentService paymentService; + + private final CartService cartService; + private final OrderService orderService; + private final PaymentService paymentService; public ShoppingFacade() { - productCatalog = new ArrayList<>(); - productCatalog.add(new Product(1, "Wireless Mouse", 25.99, "Ergonomic wireless mouse with USB receiver.")); - productCatalog.add(new Product(2, "Gaming Keyboard", 79.99, "RGB mechanical gaming keyboard with programmable keys.")); - cart = new ArrayList<>(); + Map productCatalog = new HashMap<>(); + productCatalog.put(1, new Product(1, "Wireless Mouse", 25.99, "Ergonomic wireless mouse with USB receiver.")); + productCatalog.put(2, new Product(2, "Gaming Keyboard", 79.99, "RGB mechanical gaming keyboard with programmable keys.")); + Map cart = new HashMap<>(); cartService = new CartService(cart, productCatalog); orderService = new OrderService(cart); paymentService = new PaymentService(); } + public Map getCart() { + return this.cartService.getCart(); + } + public void addToCart(int productId) { this.cartService.addToCart(productId); } - + + public void removeFromCart(int productId) { this.cartService.removeFromCart(productId); } @@ -86,10 +90,21 @@ public class ShoppingFacade { this.orderService.order(); } - public void selectPaymentMethod(String method) { - this.paymentService.selectPaymentMethod(method); + public Boolean isPaymentRequired() { + double total = this.orderService.getTotal(); + if (total == 0.0) { + LOGGER.info("No payment required"); + return false; + } + return true; + } + + public void processPayment(String method) { + Boolean isPaymentRequired = isPaymentRequired(); + if (Boolean.TRUE.equals(isPaymentRequired)) { + paymentService.selectPaymentMethod(method); + } } -} ``` Console output for starting the `App` class's `main` method: diff --git a/session-facade/pom.xml b/session-facade/pom.xml index d7e5ad956d41..2dfe12e0c80c 100644 --- a/session-facade/pom.xml +++ b/session-facade/pom.xml @@ -36,12 +36,6 @@ session-facade - - - 17 - 17 - UTF-8 - org.junit.jupiter @@ -54,5 +48,23 @@ test - + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.sessionfacade.App + + + + + + + + \ No newline at end of file diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/App.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/App.java index 985648d7c418..4f136b9fa360 100644 --- a/session-facade/src/main/java/com/iluwatar/sessionfacade/App.java +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/App.java @@ -26,18 +26,37 @@ package com.iluwatar.sessionfacade; /** - * The type App. + * The main entry point of the application that demonstrates the usage + * of the ShoppingFacade to manage the shopping process using the Session Facade pattern. + * This class serves as a client that interacts with the simplified + * interface provided by the ShoppingFacade, which encapsulates + * complex interactions with the underlying business services. + * The ShoppingFacade acts as a session bean that coordinates the communication + * between multiple services, hiding their complexity and providing a single, unified API. */ public class App { /** - * The entry point of application. + * The entry point of the application. + * This method demonstrates how the ShoppingFacade, acting as a Session Facade, is used to: + * - Add items to the shopping cart + * - Process a payment + * - Place the order + * The session facade manages the communication between the individual services + * and simplifies the interactions for the client. * * @param args the input arguments */ public static void main(String[] args) { ShoppingFacade shoppingFacade = new ShoppingFacade(); + + // Adding items to the shopping cart shoppingFacade.addToCart(1); + shoppingFacade.addToCart(2); + + // Processing the payment with the chosen method + shoppingFacade.processPayment("cash"); + + // Finalizing the order shoppingFacade.order(); - shoppingFacade.selectPaymentMethod("cash"); } -} \ No newline at end of file +} diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/CartService.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/CartService.java index 7a091f879e56..4bf9bde261cf 100644 --- a/session-facade/src/main/java/com/iluwatar/sessionfacade/CartService.java +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/CartService.java @@ -25,18 +25,24 @@ package com.iluwatar.sessionfacade; -import java.util.List; -import lombok.extern.slf4j.Slf4j; +import java.util.Map; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; /** * The type Cart service. + * Represents the cart entity, has add to cart and remove from cart methods */ @Slf4j public class CartService { - - private final List cart; - private final List productCatalog; + /** + * -- GETTER -- + * Gets cart. + */ + @Getter + private final Map cart; + private final Map productCatalog; /** * Instantiates a new Cart service. @@ -44,7 +50,7 @@ public class CartService { * @param cart the cart * @param productCatalog the product catalog */ - public CartService(List cart, List productCatalog) { + public CartService(Map cart, Map productCatalog) { this.cart = cart; this.productCatalog = productCatalog; } @@ -55,14 +61,13 @@ public CartService(List cart, List productCatalog) { * @param productId the product id */ public void addToCart(int productId) { - for (Product product : productCatalog) { - if (productId == product.id()) { - this.cart.add(product); - LOGGER.info("{} successfully added to the cart", product); - return; - } + Product product = productCatalog.get(productId); + if (product != null) { + cart.put(productId, product); + LOGGER.info("{} successfully added to the cart", product); + } else { + LOGGER.info("No product is found in catalog with id {}", productId); } - LOGGER.info("No product is found with id {}", productId); } /** @@ -71,13 +76,12 @@ public void addToCart(int productId) { * @param productId the product id */ public void removeFromCart(int productId) { - for (Product product : productCatalog) { - if (productId == product.id()) { - this.cart.remove(product); - LOGGER.info("{} successfully removed from the cart", product); - return; - } + Product product = cart.remove(productId); // Remove product from cart + if (product != null) { + LOGGER.info("{} successfully removed from the cart", product); + } else { + LOGGER.info("No product is found in cart with id {}", productId); } - LOGGER.info("No product is found with the id {}", productId); } + } diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/OrderService.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/OrderService.java index 5a317ab61679..9ffa94b7148a 100644 --- a/session-facade/src/main/java/com/iluwatar/sessionfacade/OrderService.java +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/OrderService.java @@ -25,23 +25,27 @@ package com.iluwatar.sessionfacade; -import java.util.List; +import java.util.Map; import lombok.extern.slf4j.Slf4j; - /** - * The type Order service. + * The OrderService class is responsible for finalizing a customer's order. + * It includes a method to calculate the total cost of the order, which follows + * the information expert principle from GRASP by assigning the responsibility + * of total calculation to this service. + * Additionally, it provides a method to complete the order, which empties the + * client's shopping cart once the order is finalized. */ @Slf4j public class OrderService { - private final List cart; + private final Map cart; /** * Instantiates a new Order service. * * @param cart the cart */ - public OrderService(List cart) { + public OrderService(Map cart) { this.cart = cart; } @@ -49,7 +53,31 @@ public OrderService(List cart) { * Order. */ public void order() { - LOGGER.info("Client has chosen to order {}", cart); - cart.clear(); + Double total = getTotal(); + if (!this.cart.isEmpty()) { + LOGGER.info("Client has chosen to order {} with total {}", cart, + String.format("%.2f", total)); + this.completeOrder(); + } else { + LOGGER.info("Client's shopping cart is empty"); + } + } + + /** + * Gets total. + * + * @return the total + */ + public double getTotal() { + final double[] total = {0.0}; + this.cart.forEach((key, product) -> total[0] += product.price()); + return total[0]; + } + + /** + * Complete order. + */ + public void completeOrder() { + this.cart.clear(); } } diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/PaymentService.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/PaymentService.java index 4d0120e4aa06..ce9ce8fed17c 100644 --- a/session-facade/src/main/java/com/iluwatar/sessionfacade/PaymentService.java +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/PaymentService.java @@ -29,10 +29,19 @@ import org.slf4j.LoggerFactory; /** - * The type Payment service. + * The PaymentService class is responsible for handling the selection and processing + * of different payment methods. It provides functionality to select a payment method + * (cash or credit card) and process the corresponding payment option. The class uses + * logging to inform the client of the selected payment method. + * It includes methods to: + * - Select the payment method based on the client's choice. + * - Process cash payments through the `cashPayment()` method. + * - Process credit card payments through the `creditCardPayment()` method. */ - public class PaymentService { + /** + * The constant LOGGER. + */ public static Logger LOGGER = LoggerFactory.getLogger(PaymentService.class); /** diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/Product.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/Product.java index 06e21cd344cb..9727a94db11e 100644 --- a/session-facade/src/main/java/com/iluwatar/sessionfacade/Product.java +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/Product.java @@ -29,12 +29,10 @@ * The type Product. */ public record Product(int id, String name, double price, String description) { - @Override public String toString() { return "ID: " + id + "\nName: " + name + "\nPrice: $" + price + "\nDescription: " + description; } - } diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/ProductCatalogService.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/ProductCatalogService.java index 3780a686b52e..cd6a997f9b01 100644 --- a/session-facade/src/main/java/com/iluwatar/sessionfacade/ProductCatalogService.java +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/ProductCatalogService.java @@ -25,20 +25,36 @@ package com.iluwatar.sessionfacade; -import java.util.List; +import java.util.Map; /** - * The type Product catalog service. + * The type ProductCatalogService. + * This class manages a catalog of products. It holds a map of products, + * where each product is identified by a unique ID. The class + * provides functionality to access and manage the products in the catalog. */ public class ProductCatalogService { - private List products; + + private final Map products; /** - * Instantiates a new Product catalog service. + * Instantiates a new ProductCatalogService. * - * @param products the products + * @param products the map of products to be used by this service */ - public ProductCatalogService(List products) { + public ProductCatalogService(Map products) { this.products = products; } + + // Additional methods to interact with products can be added here, for example: + + /** + * Retrieves a product by its ID. + * + * @param id the product ID + * @return the product corresponding to the ID + */ + public Product getProductById(int id) { + return products.get(id); + } } diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/ShoppingFacade.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/ShoppingFacade.java index cdc00d98f40c..14560518129f 100644 --- a/session-facade/src/main/java/com/iluwatar/sessionfacade/ShoppingFacade.java +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/ShoppingFacade.java @@ -25,47 +25,54 @@ package com.iluwatar.sessionfacade; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; + /** - * The type Shopping facade. + * The ShoppingFacade class provides a simplified interface for clients to interact with the shopping system. + * It acts as a facade to handle operations related to a shopping cart, order processing, and payment. + * Responsibilities: + * - Add products to the shopping cart. + * - Remove products from the shopping cart. + * - Retrieve the current shopping cart. + * - Finalize an order by calling the order service. + * - Check if a payment is required based on the order total. + * - Process payment using different payment methods (e.g., cash, credit card). + * The ShoppingFacade class delegates operations to the following services: + * - CartService: Manages the cart and product catalog. + * - OrderService: Handles the order finalization process and calculation of the total. + * - PaymentService: Handles the payment processing based on the selected payment method. */ +@Slf4j public class ShoppingFacade { - /** - * The Product catalog. - */ - List productCatalog = new ArrayList<>(); - /** - * The Cart. - */ - List cart = new ArrayList<>(); - /** - * The Cart service. - */ - CartService cartService = new CartService(cart, productCatalog); - /** - * The Order service. - */ - OrderService orderService = new OrderService(cart); - /** - * The Payment service. - */ - PaymentService paymentService = new PaymentService(); + private final CartService cartService; + private final OrderService orderService; + private final PaymentService paymentService; /** * Instantiates a new Shopping facade. */ public ShoppingFacade() { - productCatalog = new ArrayList<>(); - productCatalog.add(new Product(1, "Wireless Mouse", 25.99, "Ergonomic wireless mouse with USB receiver.")); - productCatalog.add(new Product(2, "Gaming Keyboard", 79.99, "RGB mechanical gaming keyboard with programmable keys.")); - cart = new ArrayList<>(); + Map productCatalog = new HashMap<>(); + productCatalog.put(1, new Product(1, "Wireless Mouse", 25.99, "Ergonomic wireless mouse with USB receiver.")); + productCatalog.put(2, new Product(2, "Gaming Keyboard", 79.99, "RGB mechanical gaming keyboard with programmable keys.")); + Map cart = new HashMap<>(); cartService = new CartService(cart, productCatalog); orderService = new OrderService(cart); paymentService = new PaymentService(); } + /** + * Gets cart. + * + * @return the cart + */ + public Map getCart() { + return this.cartService.getCart(); + } + /** * Add to cart. * @@ -92,11 +99,28 @@ public void order() { } /** - * Select payment method. + * Is payment required boolean. + * + * @return the boolean + */ + public Boolean isPaymentRequired() { + double total = this.orderService.getTotal(); + if (total == 0.0) { + LOGGER.info("No payment required"); + return false; + } + return true; + } + + /** + * Process payment. * * @param method the method */ - public void selectPaymentMethod(String method) { - this.paymentService.selectPaymentMethod(method); + public void processPayment(String method) { + Boolean isPaymentRequired = isPaymentRequired(); + if (Boolean.TRUE.equals(isPaymentRequired)) { + paymentService.selectPaymentMethod(method); + } } } diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/CartServiceTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/CartServiceTest.java index 6f0c48e51f6e..958bd20f9e25 100644 --- a/session-facade/src/test/java/com/iluwatar/sessionfacade/CartServiceTest.java +++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/CartServiceTest.java @@ -30,7 +30,9 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static org.junit.jupiter.api.Assertions.*; @@ -41,7 +43,7 @@ class CartServiceTest { private CartService cartService; - private List cart; + private Map cart; /** * Sets up. @@ -49,10 +51,10 @@ class CartServiceTest { @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); - cart = new ArrayList<>(); - List productCatalog = new ArrayList<>(); - productCatalog.add(new Product(1, "Product A", 2.0, "any description")); - productCatalog.add(new Product(2, "Product B", 300.0, "a watch")); + cart = new HashMap<>(); + Map productCatalog = new HashMap<>(); + productCatalog.put(1,new Product(1, "Product A", 2.0, "any description")); + productCatalog.put(2,new Product(2, "Product B", 300.0, "a watch")); cartService = new CartService(cart, productCatalog); } @@ -63,7 +65,7 @@ void setUp() { void testAddToCart() { cartService.addToCart(1); assertEquals(1, cart.size()); - assertEquals("Product A", cart.get(0).name()); + assertEquals("Product A", cart.get(1).name()); } /** diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java index fac4b39fbfb8..583b8a6f9e91 100644 --- a/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java +++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java @@ -30,10 +30,17 @@ import static org.mockito.Mockito.*; +/** + * The type Payment service test. + */ class PaymentServiceTest { private PaymentService paymentService; + private OrderService orderService; private Logger mockLogger; + /** + * Sets up. + */ @BeforeEach void setUp() { paymentService = new PaymentService(); @@ -41,6 +48,9 @@ void setUp() { paymentService.LOGGER = mockLogger; } + /** + * Test select cash payment method. + */ @Test void testSelectCashPaymentMethod() { String method = "cash"; @@ -48,6 +58,9 @@ void testSelectCashPaymentMethod() { verify(mockLogger).info("Client have chosen cash payment option"); } + /** + * Test select credit card payment method. + */ @Test void testSelectCreditCardPaymentMethod() { String method = "credit"; @@ -55,6 +68,9 @@ void testSelectCreditCardPaymentMethod() { verify(mockLogger).info("Client have chosen credit card payment option"); } + /** + * Test select unspecified payment method. + */ @Test void testSelectUnspecifiedPaymentMethod() { String method = "cheque"; diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/ShoppingFacadeTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/ShoppingFacadeTest.java index f5fffd49db79..01e4cb588e9c 100644 --- a/session-facade/src/test/java/com/iluwatar/sessionfacade/ShoppingFacadeTest.java +++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/ShoppingFacadeTest.java @@ -27,6 +27,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.List; +import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -47,10 +48,10 @@ void setUp() { void testAddToCart() { shoppingFacade.addToCart(1); shoppingFacade.addToCart(2); - List cart = shoppingFacade.cart; + Map cart = shoppingFacade.getCart(); assertEquals(2, cart.size(), "Cart should contain two items."); - assertEquals("Wireless Mouse", cart.get(0).name(), "First item in the cart should be 'Wireless Mouse'."); - assertEquals("Gaming Keyboard", cart.get(1).name(), "Second item in the cart should be 'Gaming Keyboard'."); + assertEquals("Wireless Mouse", cart.get(1).name(), "First item in the cart should be 'Wireless Mouse'."); + assertEquals("Gaming Keyboard", cart.get(2).name(), "Second item in the cart should be 'Gaming Keyboard'."); } @Test @@ -58,16 +59,17 @@ void testRemoveFromCart() { shoppingFacade.addToCart(1); shoppingFacade.addToCart(2); shoppingFacade.removeFromCart(1); - List cart = shoppingFacade.cart; + Map cart = shoppingFacade.getCart(); assertEquals(1, cart.size(), "Cart should contain one item after removal."); - assertEquals("Gaming Keyboard", cart.get(0).name(), "Remaining item should be 'Gaming Keyboard'."); + assertEquals("Gaming Keyboard", cart.get(2).name(), "Remaining item should be 'Gaming Keyboard'."); } @Test void testOrder() { shoppingFacade.addToCart(1); shoppingFacade.addToCart(2); + shoppingFacade.processPayment("cash"); shoppingFacade.order(); - assertTrue(shoppingFacade.cart.isEmpty(), "Cart should be empty after placing the order."); + assertTrue(shoppingFacade.getCart().isEmpty(), "Cart should be empty after placing the order."); } }