diff --git a/data-transfer-object-enum-impl/README.md b/data-transfer-object-enum-impl/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7e6dbc365319ac6741f3b54a3512a28948b949b9 --- /dev/null +++ b/data-transfer-object-enum-impl/README.md @@ -0,0 +1,54 @@ +--- +layout: pattern +title: Data Transfer Object +folder: data-transfer-object +permalink: /patterns/data-transfer-object/ +categories: Architectural +tags: + - Performance +--- + +## Intent + +Pass data with multiple attributes in one shot from client to server, to avoid multiple calls to +remote server. + +## Explanation + +Real world example + +> We need to fetch information about customers from remote database. Instead of querying the +> attributes one at a time, we use DTOs to transfer all the relevant attributes in a single shot. + +In plain words + +> Using DTO relevant information can be fetched with a single backend query. + +Wikipedia says + +> In the field of programming a data transfer object (DTO) is an object that carries data between +> processes. The motivation for its use is that communication between processes is usually done +> resorting to remote interfaces (e.g. web services), where each call is an expensive operation. +> Because the majority of the cost of each call is related to the round-trip time between the client +> and the server, one way of reducing the number of calls is to use an object (the DTO) that +> aggregates the data that would have been transferred by the several calls, but that is served by +> one call only. + +## Class diagram + +![alt text](./etc/dto-enum-uml.png "data-transfer-object") + +## Applicability + +Use the Data Transfer Object pattern when: + +* The client is asking for multiple information. And the information is related. +* When you want to boost the performance to get resources. +* You want reduced number of remote calls. + +## Credits + +* [Design Pattern - Transfer Object Pattern](https://www.tutorialspoint.com/design_pattern/transfer_object_pattern.htm) +* [Data Transfer Object](https://msdn.microsoft.com/en-us/library/ff649585.aspx) +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=f27d2644fbe5026ea448791a8ad09c94) +* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321127420&linkCode=as2&tag=javadesignpat-20&linkId=014237a67c9d46f384b35e10151956bd) diff --git a/data-transfer-object-enum-impl/etc/data-transfer-object-enum-impl.urm.puml b/data-transfer-object-enum-impl/etc/data-transfer-object-enum-impl.urm.puml new file mode 100644 index 0000000000000000000000000000000000000000..e6426c3928dfaec63e2b46de27cb61abd99f8ffe --- /dev/null +++ b/data-transfer-object-enum-impl/etc/data-transfer-object-enum-impl.urm.puml @@ -0,0 +1,129 @@ +@startuml +package com.iluwatar.datatransferenum { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + } + class Product { + - cost : Double + - id : Long + - name : String + - price : Double + - supplier : String + + Product() + + getCost() : Double + + getId() : Long + + getName() : String + + getPrice() : Double + + getSupplier() : String + + setCost(cost : Double) : Product + + setId(id : Long) : Product + + setName(name : String) : Product + + setPrice(price : Double) : Product + + setSupplier(supplier : String) : Product + + toString() : String + } + enum ProductDTO { + + valueOf(name : String) : ProductDTO {static} + + values() : ProductDTO[] {static} + } + -interface Cost { + + getCost() : Double {abstract} + } + -interface Id { + + getId() : Long {abstract} + } + -interface Name { + + getName() : String {abstract} + } + -interface Price { + + getPrice() : Double {abstract} + } + enum Request { + + valueOf(name : String) : Request {static} + + values() : Request[] {static} + } + class Create { + - cost : Double + - name : String + - price : Double + - supplier : String + + Create() + + getCost() : Double + + getName() : String + + getPrice() : Double + + getSupplier() : String + + setCost(cost : Double) : Create + + setName(name : String) : Create + + setPrice(price : Double) : Create + + setSupplier(supplier : String) : Create + } + enum Response { + + valueOf(name : String) : Response {static} + + values() : Response[] {static} + } + class Private { + - cost : Double + - id : Long + - name : String + - price : Double + + Private() + + getCost() : Double + + getId() : Long + + getName() : String + + getPrice() : Double + + setCost(cost : Double) : Private + + setId(id : Long) : Private + + setName(name : String) : Private + + setPrice(price : Double) : Private + + toString() : String + } + class Public { + - id : Long + - name : String + - price : Double + + Public() + + getId() : Long + + getName() : String + + getPrice() : Double + + setId(id : Long) : Public + + setName(name : String) : Public + + setPrice(price : Double) : Public + + toString() : String + } + -interface Supplier { + + getSupplier() : String {abstract} + } + class ProductResource { + - products : List + + ProductResource(products : List) + + getAllProductsForAdmin() : List + + getAllProductsForCustomer() : List + + getProducts() : List + + save(createProductDTO : Create) + } +} +Create ..+ Request +Request ..+ ProductDTO +Private ..+ Response +Supplier ..+ ProductDTO +Name ..+ ProductDTO +ProductResource --> "-products" Product +Public ..+ Response +Id ..+ ProductDTO +Price ..+ ProductDTO +Response ..+ ProductDTO +Cost ..+ ProductDTO +Create ..|> Name +Create ..|> Price +Create ..|> Cost +Create ..|> Supplier +Private ..|> Id +Private ..|> Name +Private ..|> Price +Private ..|> Cost +Public ..|> Id +Public ..|> Name +Public ..|> Price +@enduml diff --git a/data-transfer-object-enum-impl/etc/dto-enum-uml.png b/data-transfer-object-enum-impl/etc/dto-enum-uml.png new file mode 100644 index 0000000000000000000000000000000000000000..57e9a1ab520db212a7ff5635c5b1b2125af7ffe6 Binary files /dev/null and b/data-transfer-object-enum-impl/etc/dto-enum-uml.png differ diff --git a/data-transfer-object-enum-impl/pom.xml b/data-transfer-object-enum-impl/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..74c8a3a6cf8b708a22503a1c2b60e5541f379cea --- /dev/null +++ b/data-transfer-object-enum-impl/pom.xml @@ -0,0 +1,61 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.24.0-SNAPSHOT + + data-transfer-object-enum-impl + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.datatransferenum.App + + + + + + + + + diff --git a/data-transfer-object-enum-impl/src/main/java/com/iluwatar/datatransferenum/App.java b/data-transfer-object-enum-impl/src/main/java/com/iluwatar/datatransferenum/App.java new file mode 100644 index 0000000000000000000000000000000000000000..9c80d07cc461ea3958b1e3e0df5ab71cd06e7e70 --- /dev/null +++ b/data-transfer-object-enum-impl/src/main/java/com/iluwatar/datatransferenum/App.java @@ -0,0 +1,59 @@ +package com.iluwatar.datatransferenum; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The Data Transfer Object pattern is a design pattern in which an data transfer object is used to + * serve related information together to avoid multiple call for each piece of information. + * + *

In this example, ({@link App}) as as product details consumer i.e. client to + * request for product details to server. + * + *

productResource ({@link ProductResource}) act as server to serve product information. And + * The productDto ({@link ProductDto} is data transfer object to share product information. + */ +public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + /** + * Method as act client and request to server for details. + * + * @param args program argument. + */ + public static void main(String[] args) { + Product tv = + new Product().setId(1L).setName("TV").setSupplier("Sony").setPrice(1000D).setCost(1090D); + Product microwave = + new Product().setId(2L).setName("microwave").setSupplier("Delonghi").setPrice(1000D) + .setCost(1090D); + Product refrigerator = + new Product().setId(3L).setName("refrigerator").setSupplier("Botsch").setPrice(1000D) + .setCost(1090D); + Product airConditioner = + new Product().setId(4L).setName("airConditioner").setSupplier("LG").setPrice(1000D) + .setCost(1090D); + List products = + new ArrayList<>(Arrays.asList(tv, microwave, refrigerator, airConditioner)); + ProductResource productResource = new ProductResource(products); + + LOGGER.info("####### List of products including sensitive data just for admins: \n {}", + Arrays.toString(productResource.getAllProductsForAdmin().toArray())); + LOGGER.info("####### List of products for customers: \n {}", + Arrays.toString(productResource.getAllProductsForCustomer().toArray())); + + LOGGER.info("####### Going to save Sony PS5 ..."); + ProductDto.Request.Create createProductRequestDto = new ProductDto.Request.Create() + .setName("PS5") + .setCost(1000D) + .setPrice(1220D) + .setSupplier("Sony"); + productResource.save(createProductRequestDto); + LOGGER.info("####### List of products after adding PS5: {}", + Arrays.toString(productResource.getProducts().toArray())); + } +} diff --git a/data-transfer-object-enum-impl/src/main/java/com/iluwatar/datatransferenum/Product.java b/data-transfer-object-enum-impl/src/main/java/com/iluwatar/datatransferenum/Product.java new file mode 100644 index 0000000000000000000000000000000000000000..96758baf4297cdc82dcde70689b8f8796d7f21ed --- /dev/null +++ b/data-transfer-object-enum-impl/src/main/java/com/iluwatar/datatransferenum/Product.java @@ -0,0 +1,91 @@ +package com.iluwatar.datatransferenum; + +/** + * {@link Product} is a entity class for product entity. This class act as entity in the demo. + */ +public final class Product { + private Long id; + private String name; + private Double price; + private Double cost; + private String supplier; + + /** + * Constructor. + * + * @param id product id + * @param name product name + * @param price product price + * @param cost product cost + * @param supplier product supplier + */ + public Product(Long id, String name, Double price, Double cost, String supplier) { + this.id = id; + this.name = name; + this.price = price; + this.cost = cost; + this.supplier = supplier; + } + + /** + * Constructor. + */ + public Product() { + } + + public Long getId() { + return id; + } + + public Product setId(Long id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public Product setName(String name) { + this.name = name; + return this; + } + + public Double getPrice() { + return price; + } + + public Product setPrice(Double price) { + this.price = price; + return this; + } + + public Double getCost() { + return cost; + } + + public Product setCost(Double cost) { + this.cost = cost; + return this; + } + + public String getSupplier() { + return supplier; + } + + public Product setSupplier(String supplier) { + this.supplier = supplier; + return this; + } + + @Override + public String toString() { + return "Product{" + + "id=" + id + + ", name='" + name + '\'' + + ", price=" + price + + ", cost=" + cost + + ", supplier='" + supplier + '\'' + + '}'; + } +} diff --git a/data-transfer-object-enum-impl/src/main/java/com/iluwatar/datatransferenum/ProductDto.java b/data-transfer-object-enum-impl/src/main/java/com/iluwatar/datatransferenum/ProductDto.java new file mode 100644 index 0000000000000000000000000000000000000000..49d08d896c29f269a797059c2d042041d67e87c3 --- /dev/null +++ b/data-transfer-object-enum-impl/src/main/java/com/iluwatar/datatransferenum/ProductDto.java @@ -0,0 +1,264 @@ +package com.iluwatar.datatransferenum; + +/** + * {@link ProductDto} is a data transfer object POJO. + * Instead of sending individual information to + * client We can send related information together in POJO. + * + *

Dto will not have any business logic in it. + */ +public enum ProductDto { + ; + + /** + * This is Request class which consist of Create or any other request DTO's + * you might want to use in your API. + */ + public enum Request { + ; + + /** + * This is Create dto class for requesting create new product. + */ + public static final class Create implements Name, Price, Cost, Supplier { + private String name; + private Double price; + private Double cost; + private String supplier; + + @Override + public String getName() { + return name; + } + + public Create setName(String name) { + this.name = name; + return this; + } + + @Override + public Double getPrice() { + return price; + } + + public Create setPrice(Double price) { + this.price = price; + return this; + } + + @Override + public Double getCost() { + return cost; + } + + public Create setCost(Double cost) { + this.cost = cost; + return this; + } + + @Override + public String getSupplier() { + return supplier; + } + + public Create setSupplier(String supplier) { + this.supplier = supplier; + return this; + } + } + } + + /** + * This is Response class which consist of any response DTO's + * you might want to provide to your clients. + */ + public enum Response { + ; + + /** + * This is Public dto class for API response with the lowest data security. + */ + public static final class Public implements Id, Name, Price { + private Long id; + private String name; + private Double price; + + @Override + public Long getId() { + return id; + } + + public Public setId(Long id) { + this.id = id; + return this; + } + + @Override + public String getName() { + return name; + } + + public Public setName(String name) { + this.name = name; + return this; + } + + @Override + public Double getPrice() { + return price; + } + + public Public setPrice(Double price) { + this.price = price; + return this; + } + + @Override + public String toString() { + return "Public{" + + "id=" + + id + + ", name='" + + name + + '\'' + + ", price=" + + price + + '}'; + } + } + + /** + * This is Private dto class for API response with the highest data security. + */ + public static final class Private implements Id, Name, Price, Cost { + private Long id; + private String name; + private Double price; + private Double cost; + + @Override + public Long getId() { + return id; + } + + public Private setId(Long id) { + this.id = id; + return this; + } + + @Override + public String getName() { + return name; + } + + public Private setName(String name) { + this.name = name; + return this; + } + + @Override + public Double getPrice() { + return price; + } + + public Private setPrice(Double price) { + this.price = price; + return this; + } + + @Override + public Double getCost() { + return cost; + } + + public Private setCost(Double cost) { + this.cost = cost; + return this; + } + + @Override + public String toString() { + return "Private{" + + + "id=" + + id + + + ", name='" + + name + + '\'' + + + ", price=" + + price + + + ", cost=" + + cost + + + '}'; + } + } + } + + /** + * Use this interface whenever you want to provide the product Id in your DTO. + */ + private interface Id { + /** + * Unique identifier of the product. + * + * @return : id of the product. + */ + Long getId(); + } + + /** + * Use this interface whenever you want to provide the product Name in your DTO. + */ + private interface Name { + /** + * The name of the product. + * + * @return : name of the product. + */ + String getName(); + } + + /** + * Use this interface whenever you want to provide the product Price in your DTO. + */ + private interface Price { + /** + * The amount we sell a product for. + * This data is not confidential + * + * @return : price of the product. + */ + Double getPrice(); + } + + /** + * Use this interface whenever you want to provide the product Cost in your DTO. + */ + private interface Cost { + /** + * The amount that it costs us to purchase this product + * For the amount we sell a product for, see the {@link Price Price} parameter. + * This data is confidential + * + * @return : cost of the product. + */ + Double getCost(); + } + + /** + * Use this interface whenever you want to provide the product Supplier in your DTO. + */ + private interface Supplier { + /** + * The name of supplier of the product or its manufacturer. + * This data is highly confidential + * + * @return : supplier of the product. + */ + String getSupplier(); + } +} diff --git a/data-transfer-object-enum-impl/src/main/java/com/iluwatar/datatransferenum/ProductResource.java b/data-transfer-object-enum-impl/src/main/java/com/iluwatar/datatransferenum/ProductResource.java new file mode 100644 index 0000000000000000000000000000000000000000..5940982b1f86c2b0b51f0cff08ead83d1402f3da --- /dev/null +++ b/data-transfer-object-enum-impl/src/main/java/com/iluwatar/datatransferenum/ProductResource.java @@ -0,0 +1,71 @@ +package com.iluwatar.datatransferenum; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * The resource class which serves product information. This class act as server in the demo. Which + * has all product details. + */ +public class ProductResource { + private final List products; + + /** + * Initialise resource with existing products. + * + * @param products initialize resource with existing products. Act as database. + */ + public ProductResource(final List products) { + this.products = products; + } + + /** + * Get all products. + * + * @return : all products in list but in the scheme of private dto. + */ + public List getAllProductsForAdmin() { + return products + .stream() + .map(p -> new ProductDto.Response.Private().setId(p.getId()).setName(p.getName()) + .setCost(p.getCost()) + .setPrice(p.getPrice())) + .collect(Collectors.toList()); + } + + /** + * Get all products. + * + * @return : all products in list but in the scheme of public dto. + */ + public List getAllProductsForCustomer() { + return products + .stream() + .map(p -> new ProductDto.Response.Public().setId(p.getId()).setName(p.getName()) + .setPrice(p.getPrice())) + .collect(Collectors.toList()); + } + + /** + * Save new product. + * + * @param createProductDto save new product to list. + */ + public void save(ProductDto.Request.Create createProductDto) { + products.add(new Product() + .setId((long) (products.size() + 1)) + .setName(createProductDto.getName()) + .setSupplier(createProductDto.getSupplier()) + .setPrice(createProductDto.getPrice()) + .setCost(createProductDto.getCost())); + } + + /** + * List of all products in an entity representation. + * + * @return : all the products entity that stored in the products list + */ + public List getProducts() { + return products; + } +} diff --git a/data-transfer-object-enum-impl/src/test/java/com/iluwatar/datatransferenum/AppTest.java b/data-transfer-object-enum-impl/src/test/java/com/iluwatar/datatransferenum/AppTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bba131e641637b9682bba5070a6b1884f260dd0f --- /dev/null +++ b/data-transfer-object-enum-impl/src/test/java/com/iluwatar/datatransferenum/AppTest.java @@ -0,0 +1,44 @@ +/* + * The MIT License + * Copyright © 2014-2019 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.datatransferenum; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + + +import org.junit.jupiter.api.Test; + +class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + *

+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])} + * throws an exception. + */ + + @Test + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[] {})); + } +} diff --git a/pom.xml b/pom.xml index 86cefa13386a8d7fa2c3c092127969a65f82178b..b6556245b72bf10b94ebc1a3dbb0f58f3282e3ae 100644 --- a/pom.xml +++ b/pom.xml @@ -199,6 +199,7 @@ filterer factory separated-interface + data-transfer-object-enum-impl