未验证 提交 5bc61c8c 编写于 作者: S siavash 提交者: GitHub

#1569 DTO pattern implementation using Enums (#1570)

* #1569 DTO pattern implemented using Enums

* #1569 DTO pattern implemented using Enums

* #1569 adding some java docs

* #1569 some changes in java doc and code style

* #1569 some changes in java doc and code style

* #1569 some changes in java doc and code style

* #1569 some changes in java doc and code style

* #1569 adding suggested extra line

* #1569 license added to pom.xml

* #1569 more checkstyle problems resolved

* #1569 more checkstyle problems resolved

* #1569 more checkstyle problems resolved
Co-authored-by: Nsiavashsoleymani <siavash.soleimani@snapp.cab>
Co-authored-by: NSubhrodip Mohanta <subhrodipmohanta@gmail.com>
上级 a94615ac
---
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)
@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<Product>
+ ProductResource(products : List<Product>)
+ getAllProductsForAdmin() : List<Private>
+ getAllProductsForCustomer() : List<Public>
+ getProducts() : List<Product>
+ 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
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.24.0-SNAPSHOT</version>
</parent>
<artifactId>data-transfer-object-enum-impl</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.datatransferenum.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
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.
*
* <p>In this example, ({@link App}) as as product details consumer i.e. client to
* request for product details to server.
*
* <p>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<Product> 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()));
}
}
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 + '\''
+ '}';
}
}
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.
*
* <p>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.
* <b>This data is not confidential</b>
*
* @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.
* <b>This data is confidential</b>
*
* @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.
* <b>This data is highly confidential</b>
*
* @return : supplier of the product.
*/
String getSupplier();
}
}
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<Product> products;
/**
* Initialise resource with existing products.
*
* @param products initialize resource with existing products. Act as database.
*/
public ProductResource(final List<Product> products) {
this.products = products;
}
/**
* Get all products.
*
* @return : all products in list but in the scheme of private dto.
*/
public List<ProductDto.Response.Private> 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<ProductDto.Response.Public> 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<Product> getProducts() {
return products;
}
}
/*
* 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.
* <p>
* 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[] {}));
}
}
......@@ -199,6 +199,7 @@
<module>filterer</module>
<module>factory</module>
<module>separated-interface</module>
<module>data-transfer-object-enum-impl</module>
</modules>
<repositories>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册