docs: combine embedded value to value object

This commit is contained in:
Ilkka Seppälä
2024-05-25 16:44:08 +03:00
parent e3c3b055f5
commit 52eb5c7d69
12 changed files with 23 additions and 842 deletions
-146
View File
@@ -1,146 +0,0 @@
---
title: Embedded Value
category: Behavioral
language: en
tag:
- Data access
- Enterprise patterns
- Optimization
- Performance
---
## Also known as
* Aggregate Mapping
* Composer
* Inline Value
* Integrated Value
## Intent
The Embedded Value design pattern aims to enhance performance and reduce memory overhead by storing frequently accessed immutable data directly within the object that uses it, rather than separately.
## Explanation
Real-world example
> In a library, the reference desk embeds commonly used resources like dictionaries and encyclopedias directly at the desk for quick and easy access, similar to how the Embedded Value design pattern integrates frequently used data directly within an object for efficiency.
In plain words
> Embedded value pattern let's you map an object into several fields of another objects table.
**Programmatic Example**
Consider an online ordering example where we have details of item ordered and shipping address. We have Shipping address embedded in Order object. But in database we map shipping address values in Order record instead of creating a separate table for Shipping address and using foreign key to reference the order object.
First, we have POJOs `Order` and `ShippingAddress`
```java
public class Order {
private int id;
private String item;
private String orderedBy;
private ShippingAddress ShippingAddress;
public Order(String item, String orderedBy, ShippingAddress ShippingAddress) {
this.item = item;
this.orderedBy = orderedBy;
this.ShippingAddress = ShippingAddress;
}
}
```
```java
public class ShippingAddress {
private String city;
private String state;
private String pincode;
public ShippingAddress(String city, String state, String pincode) {
this.city = city;
this.state = state;
this.pincode = pincode;
}
}
```
Now, we have to create only one table for Order along with fields for shipping address attributes.
```Sql
CREATE TABLE Orders
(
Id INT AUTO_INCREMENT,
item VARCHAR(50) NOT NULL,
orderedBy VARCHAR(50) city VARCHAR (50),
state VARCHAR(50),
pincode CHAR(6) NOT NULL,
PRIMARY KEY (Id)
)
```
While performing the database queries and inserts, we box and unbox shipping address details.
```java
final String INSERT_ORDER="INSERT INTO Orders (item, orderedBy, city, state, pincode) VALUES (?, ?, ?, ?, ?)";
public boolean insertOrder(Order order)throws Exception{
var insertOrder=new PreparedStatement(INSERT_ORDER);
var address=order.getShippingAddress();
conn.setAutoCommit(false);
insertIntoOrders.setString(1,order.getItem());
insertIntoOrders.setString(2,order.getOrderedBy());
insertIntoOrders.setString(3,address.getCity());
insertIntoOrders.setString(4,address.getState());
insertIntoOrders.setString(5,address.getPincode());
var affectedRows=insertIntoOrders.executeUpdate();
if(affectedRows==1) {
Logger.info("Inserted successfully");
} else {
Logger.info("Couldn't insert "+order);
}
}
```
## Class diagram
![Embedded Value](./etc/embedded-value.urm.png "Embedded Value class diagram")
## Applicability
Use the Embedded value pattern when:
* An application requires high performance and the data involved is immutable.
* Memory footprint reduction is critical, especially in environments with limited resources.
* Objects frequently access a particular piece of immutable data.
## Tutorials
* [Dzone](https://dzone.com/articles/practical-php-patterns/practical-php-patterns-3)
* [Ram N Java](https://ramj2ee.blogspot.com/2013/08/embedded-value-design-pattern.html)
* [Five's Weblog](https://powerdream5.wordpress.com/2007/10/09/embedded-value/)
## Consequences
Benefits:
* Reduces the memory overhead by avoiding separate allocations for immutable data.
* Improves performance by minimizing memory accesses and reducing cache misses.
Trade-offs:
* Increases complexity in object design and can lead to tightly coupled systems.
* Modifying the embedded value necessitates changes across all objects that embed this value, which can complicate maintenance.
## Related Patterns
[Flyweight](https://java-design-patterns.com/patterns/flyweight/): Shares objects to support large quantities using a minimal amount of memory, somewhat similar in intent but different in implementation.
[Singleton](https://java-design-patterns.com/patterns/singleton/): Ensures a class has only one instance and provides a global point of access to it, can be used to manage a shared embedded value.
## Credits
* [Patterns of Enterprise Application Architecture](https://amzn.to/4452Idd)
* [Ram N Java](https://ramj2ee.blogspot.com/2013/08/embedded-value-design-pattern.html)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

@@ -1,79 +0,0 @@
@startuml
package com.iluwatar.embedded.value {
class App {
- LOGGER : Logger {static}
+ App()
+ main(args : String[]) {static}
}
class DataSource {
- LOGGER : Logger {static}
- conn : Connection
- createschema : Statement
- deleteschema : Statement
- getschema : Statement
- insertIntoOrders : PreparedStatement
- queryOrders : Statement
- queyOrderByID : PreparedStatement
- removeorder : PreparedStatement
+ DataSource()
+ createSchema() : boolean
+ deleteSchema() : boolean
+ getSchema() : String
+ insertOrder(order : Order) : boolean
+ queryOrder(id : int) : Order
+ queryOrders() : Stream<Order>
+ removeOrder(id : int)
}
~interface DataSourceInterface {
+ CREATE_SCHEMA : String {static}
+ DELETE_SCHEMA : String {static}
+ GET_SCHEMA : String {static}
+ INSERT_ORDER : String {static}
+ JDBC_URL : String {static}
+ QUERY_ORDER : String {static}
+ QUERY_ORDERS : String {static}
+ REMOVE_ORDER : String {static}
+ createSchema() : boolean {abstract}
+ deleteSchema() : boolean {abstract}
+ getSchema() : String {abstract}
+ insertOrder(Order) : boolean {abstract}
+ queryOrder(int) : Order {abstract}
+ queryOrders() : Stream<Order> {abstract}
+ removeOrder(int) {abstract}
}
class Order {
- id : int
- item : String
- orderedBy : String
- shippingAddress : ShippingAddress
+ Order()
+ Order(id : int, item : String, orderedBy : String, shippingAddress : ShippingAddress)
+ Order(item : String, orderedBy : String, shippingAddress : ShippingAddress)
+ getId() : int
+ getItem() : String
+ getOrderedBy() : String
+ getShippingAddress() : ShippingAddress
+ setId(id : int)
+ setItem(item : String)
+ setOrderedBy(orderedBy : String)
+ setShippingAddress(shippingAddress : ShippingAddress)
+ toString() : String
}
class ShippingAddress {
- city : String
- pincode : String
- state : String
+ ShippingAddress()
+ ShippingAddress(city : String, state : String, pincode : String)
+ getCity() : String
+ getPincode() : String
+ getState() : String
+ setCity(city : String)
+ setPincode(pincode : String)
+ setState(state : String)
+ toString() : String
}
}
Order --> "-shippingAddress" ShippingAddress
DataSource ..|> DataSourceInterface
@enduml
-66
View File
@@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" 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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>embedded-value</artifactId>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<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.embedded.value.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,109 +0,0 @@
/*
* 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.embedded.value;
import lombok.extern.slf4j.Slf4j;
/**
* <p> Many small objects make sense in an OO system that dont make sense as
* tables in a database. Examples include currency-aware money objects (amount, currency) and date
* ranges. Although the default thinking is to save an object as a table, no sane
* person would want a table of money values. </p>
*
* <p> An Embedded Value maps the values of an object to fields in the record of
* the objects owner. In this implementation we have an Order object with links to an
* ShippingAddress object. In the resulting table the fields in the ShippingAddress
* object map to fields in the Order table rather than make new records
* themselves. </p>
*/
@Slf4j
public class App {
private static final String BANGLORE = "Banglore";
private static final String KARNATAKA = "Karnataka";
/**
* Program entry point.
*
* @param args command line args.
* @throws Exception if any error occurs.
*
*/
public static void main(String[] args) throws Exception {
final var dataSource = new DataSource();
// Orders to insert into database
final var order1 = new Order("JBL headphone", "Ram",
new ShippingAddress(BANGLORE, KARNATAKA, "560040"));
final var order2 = new Order("MacBook Pro", "Manjunath",
new ShippingAddress(BANGLORE, KARNATAKA, "581204"));
final var order3 = new Order("Carrie Soto is Back", "Shiva",
new ShippingAddress(BANGLORE, KARNATAKA, "560004"));
// Create table for orders - Orders(id, name, orderedBy, city, state, pincode).
// We can see that table is different from the Order object we have.
// We're mapping ShippingAddress into city, state, pincode columns of the database and not creating a separate table.
if (dataSource.createSchema()) {
LOGGER.info("TABLE CREATED");
LOGGER.info("Table \"Orders\" schema:\n" + dataSource.getSchema());
} else {
//If not able to create table, there's nothing we can do further.
LOGGER.error("Error creating table");
System.exit(0);
}
// Initially, database is empty
LOGGER.info("Orders Query: {}", dataSource.queryOrders().toList());
//Insert orders where shippingAddress is mapped to different columns of the same table
dataSource.insertOrder(order1);
dataSource.insertOrder(order2);
dataSource.insertOrder(order3);
// Query orders.
// We'll create ShippingAddress object from city, state, pincode values from the table and add it to Order object
LOGGER.info("Orders Query: {}", dataSource.queryOrders().toList() + "\n");
//Query order by given id
LOGGER.info("Query Order with id=2: {}", dataSource.queryOrder(2));
//Remove order by given id.
//Since we'd mapped address in the same table, deleting order will also take out the shipping address details.
LOGGER.info("Remove Order with id=1");
dataSource.removeOrder(1);
LOGGER.info("\nOrders Query: {}", dataSource.queryOrders().toList() + "\n");
//After successful demonstration of the pattern, drop the table
if (dataSource.deleteSchema()) {
LOGGER.info("TABLE DROPPED");
} else {
//If there's a potential error while dropping table
LOGGER.error("Error deleting table");
}
}
}
@@ -1,211 +0,0 @@
/*
* 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.embedded.value;
import static java.sql.PreparedStatement.RETURN_GENERATED_KEYS;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
/**
* Communicates with H2 database with the help of JDBC API.
* Inherits the SQL queries and methods from @link AbstractDataSource class.
*/
@Slf4j
public class DataSource implements DataSourceInterface {
private Connection conn;
// Statements are objects which are used to execute queries which will not be repeated.
private Statement getschema;
private Statement deleteschema;
private Statement queryOrders;
// PreparedStatements are used to execute queries which will be repeated.
private PreparedStatement insertIntoOrders;
private PreparedStatement removeorder;
private PreparedStatement queyOrderById;
/**
* {@summary Establish connection to database.
* Constructor to create DataSource object.}
*/
public DataSource() {
try {
conn = DriverManager.getConnection(JDBC_URL);
LOGGER.info("Connected to H2 in-memory database: " + conn.getCatalog());
} catch (SQLException e) {
LOGGER.error(e.getLocalizedMessage(), e.getCause());
}
}
@Override
public boolean createSchema() {
try (Statement createschema = conn.createStatement()) {
createschema.execute(CREATE_SCHEMA);
insertIntoOrders = conn.prepareStatement(INSERT_ORDER, RETURN_GENERATED_KEYS);
getschema = conn.createStatement();
queryOrders = conn.createStatement();
removeorder = conn.prepareStatement(REMOVE_ORDER);
queyOrderById = conn.prepareStatement(QUERY_ORDER);
deleteschema = conn.createStatement();
} catch (SQLException e) {
LOGGER.error(e.getLocalizedMessage(), e.getCause());
return false;
}
return true;
}
@Override
public String getSchema() {
try {
var resultSet = getschema.executeQuery(GET_SCHEMA);
var sb = new StringBuilder();
while (resultSet.next()) {
sb.append("Col name: ").append(resultSet.getString(1)).append(", Col type: ").append(resultSet.getString(2)).append("\n");
}
getschema.close();
return sb.toString();
} catch (Exception e) {
LOGGER.error("Error in retrieving schema: {}", e.getLocalizedMessage(), e.getCause());
}
return "Schema unavailable";
}
@Override
public boolean insertOrder(Order order) {
try {
conn.setAutoCommit(false);
insertIntoOrders.setString(1, order.getItem());
insertIntoOrders.setString(2, order.getOrderedBy());
var address = order.getShippingAddress();
insertIntoOrders.setString(3, address.getCity());
insertIntoOrders.setString(4, address.getState());
insertIntoOrders.setString(5, address.getPincode());
var affectedRows = insertIntoOrders.executeUpdate();
if (affectedRows == 1) {
var rs = insertIntoOrders.getGeneratedKeys();
rs.last();
var insertedAddress = new ShippingAddress(address.getCity(), address.getState(), address.getPincode());
var insertedOrder = new Order(rs.getInt(1), order.getItem(), order.getOrderedBy(),
insertedAddress);
conn.commit();
LOGGER.info("Inserted: {}", insertedOrder);
} else {
conn.rollback();
}
} catch (Exception e) {
LOGGER.error(e.getLocalizedMessage());
} finally {
try {
conn.setAutoCommit(true);
} catch (SQLException e) {
LOGGER.error(e.getLocalizedMessage());
}
}
return true;
}
@Override
public Stream<Order> queryOrders() {
var ordersList = new ArrayList<Order>();
try (var rSet = queryOrders.executeQuery(QUERY_ORDERS)) {
while (rSet.next()) {
var order = new Order(rSet.getInt(1), rSet.getString(2), rSet.getString(3),
new ShippingAddress(rSet.getString(4), rSet.getString(5),
rSet.getString(6)));
ordersList.add(order);
}
} catch (SQLException e) {
LOGGER.error(e.getMessage(), e.getCause());
}
return ordersList.stream();
}
/**
* Query order by given id.
* @param id as the parameter
* @return Order objct
* @throws SQLException in case of unexpected events
*/
@Override
public Order queryOrder(int id) throws SQLException {
Order order = null;
queyOrderById.setInt(1, id);
try (var rSet = queyOrderById.executeQuery()) {
queyOrderById.setInt(1, id);
if (rSet.next()) {
var address = new ShippingAddress(rSet.getString(4),
rSet.getString(5), rSet.getString(6));
order = new Order(rSet.getInt(1), rSet.getString(2), rSet.getString(3), address);
}
} catch (Exception e) {
LOGGER.error(e.getLocalizedMessage(), e.getCause());
}
return order;
}
@Override
public void removeOrder(int id) throws Exception {
try {
conn.setAutoCommit(false);
removeorder.setInt(1, id);
if (removeorder.executeUpdate() == 1) {
LOGGER.info("Order with id " + id + " successfully removed");
} else {
LOGGER.info("Order with id " + id + " unavailable.");
}
} catch (Exception e) {
LOGGER.error(e.getLocalizedMessage(), e.getCause());
conn.rollback();
} finally {
conn.setAutoCommit(true);
}
}
@Override
public boolean deleteSchema() {
try {
deleteschema.execute(DELETE_SCHEMA);
queryOrders.close();
queyOrderById.close();
deleteschema.close();
insertIntoOrders.close();
conn.close();
return true;
} catch (Exception e) {
LOGGER.error(e.getLocalizedMessage(), e.getCause());
}
return false;
}
}
@@ -1,69 +0,0 @@
/*
* 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.embedded.value;
import java.sql.SQLException;
import java.util.stream.Stream;
/*
* Abstract class which contains the required SQL queries and basic methods declaration.
*
* The main thing to consider is that the ShippingAddress object doesn't have it's own class
* but it's values are stored into the Orders table as city, state, pincode
*
*/
interface DataSourceInterface {
String JDBC_URL = "jdbc:h2:mem:Embedded-Value";
String CREATE_SCHEMA = "CREATE TABLE Orders (Id INT AUTO_INCREMENT, item VARCHAR(50) NOT NULL, orderedBy VARCHAR(50)"
+ ", city VARCHAR(50), state VARCHAR(50), pincode CHAR(6) NOT NULL, PRIMARY KEY(Id))";
String GET_SCHEMA = "SHOW COLUMNS FROM Orders";
String INSERT_ORDER = "INSERT INTO Orders (item, orderedBy, city, state, pincode) VALUES(?, ?, ?, ?, ?)";
String QUERY_ORDERS = "SELECT * FROM Orders";
String QUERY_ORDER = QUERY_ORDERS + " WHERE Id = ?";
String REMOVE_ORDER = "DELETE FROM Orders WHERE Id = ?";
String DELETE_SCHEMA = "DROP TABLE Orders";
boolean createSchema() throws SQLException;
String getSchema() throws SQLException;
boolean insertOrder(Order order) throws SQLException;
Stream<Order> queryOrders() throws SQLException;
Order queryOrder(int id) throws SQLException;
void removeOrder(int id) throws Exception;
boolean deleteSchema() throws Exception;
}
@@ -1,62 +0,0 @@
/*
* 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.embedded.value;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* A POJO which represents the Order object.
*/
@ToString
@Setter
@Getter
public class Order {
private int id;
private String item;
private String orderedBy;
private ShippingAddress shippingAddress;
/**
* Constructor for Item object.
* @param item item name
* @param orderedBy item orderer
* @param shippingAddress shipping address details
*/
public Order(String item, String orderedBy, ShippingAddress shippingAddress) {
this.item = item;
this.orderedBy = orderedBy;
this.shippingAddress = shippingAddress;
}
public Order(int id, String item, String orderedBy, ShippingAddress shippingAddress) {
this(item, orderedBy, shippingAddress);
this.id = id;
}
}
@@ -1,54 +0,0 @@
/*
* 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.embedded.value;
import lombok.Getter;
import lombok.ToString;
/**
* Another POJO which wraps the Shipping details of order into Object.
*/
@ToString
@Getter
public class ShippingAddress {
private String city;
private String state;
private String pincode;
/**
* Constructor for Shipping Address object.
* @param city City name
* @param state State name
* @param pincode Pin code of the city
*
*/
public ShippingAddress(String city, String state, String pincode) {
this.city = city;
this.state = state;
this.pincode = pincode;
}
}
@@ -1,41 +0,0 @@
/*
* 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.embedded.value;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import org.junit.jupiter.api.Test;
/**
* Check whether the execution of the main method in {@link App}
* throws an exception.
*/
class AppTest {
@Test
void doesNotThrowException() {
assertDoesNotThrow(() -> App.main(new String[] {}));
}
}
-1
View File
@@ -198,7 +198,6 @@
<module>service-to-worker</module>
<module>client-session</module>
<module>model-view-intent</module>
<module>embedded-value</module>
<module>currying</module>
<module>serialized-entity</module>
<module>identity-map</module>
+23 -4
View File
@@ -1,21 +1,29 @@
---
title: Value Object
category: Creational
category: Structural
language: en
tag:
- Data access
- Data binding
- Domain
- Encapsulation
- Enterprise patterns
- Immutable
- Optimization
- Performance
- Persistence
---
## Also known as
* Embedded Value
* Immutable Object
* Inline Value
* Integrated Value
## Intent
To create immutable objects that represent a descriptive aspect of the domain with no conceptual identity.
To create immutable objects that represent a descriptive aspect of the domain with no conceptual identity. It aims to enhance performance and reduce memory overhead by storing frequently accessed immutable data directly within the object that uses it, rather than separately.
## Explanation
@@ -86,6 +94,13 @@ Use the Value Object when
* When representing a set of attributes that together describe an entity but without an identity.
* When the equality of the objects is based on the value of the properties, not the identity.
* When you need to ensure that objects cannot be altered once created.
* An application requires high performance and the data involved is immutable.
* Memory footprint reduction is critical, especially in environments with limited resources.
* Objects frequently access a particular piece of immutable data.
## Tutorials
* [VALJOs - Value Java Objects (Stephen Colebourne)](http://blog.joda.org/2014/03/valjos-value-java-objects.html)
## Known uses
@@ -102,17 +117,23 @@ Benefits:
* Simplifies code by making objects immutable.
* Thread-safe as the object's state cannot change after creation.
* Easier to reason about and maintain.
* Reduces the memory overhead by avoiding separate allocations for immutable data.
* Improves performance by minimizing memory accesses and reducing cache misses.
Trade-offs:
* Creating a new object for every change can be less efficient for complex objects.
* Increased memory usage due to the creation of multiple objects representing different states.
* Increases complexity in object design and can lead to tightly coupled systems.
* Modifying the embedded value necessitates changes across all objects that embed this value, which can complicate maintenance.
## Related Patterns
* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): Often used to create instances of value objects.
* [Flyweight](https://java-design-patterns.com/patterns/flyweight/): Shares objects to support large quantities using a minimal amount of memory, somewhat similar in intent but different in implementation.
* [Builder](https://java-design-patterns.com/patterns/builder/): Can be used to construct complex value objects step by step.
* [Prototype](https://java-design-patterns.com/patterns/prototype/): Can be used to clone existing value objects, though cloning is less common with immutable objects.
* [Singleton](https://java-design-patterns.com/patterns/singleton/): Ensures a class has only one instance and provides a global point of access to it, can be used to manage a shared embedded value.
## Credits
@@ -121,5 +142,3 @@ Trade-offs:
* [J2EE Design Patterns](https://amzn.to/4dpzgmx)
* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR)
* [ValueObject - Martin Fowler](https://martinfowler.com/bliki/ValueObject.html)
* [VALJOs - Value Java Objects: Stephen Colebourne](http://blog.joda.org/2014/03/valjos-value-java-objects.html)
* [Value Object : Wikipedia](https://en.wikipedia.org/wiki/Value_object)