mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-08-17 10:25:53 +00:00
docs: update value object
This commit is contained in:
+54
-24
@@ -3,40 +3,45 @@ title: Value Object
|
|||||||
category: Creational
|
category: Creational
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Instantiation
|
- Data binding
|
||||||
|
- Domain
|
||||||
|
- Encapsulation
|
||||||
|
- Immutable
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Also known as
|
||||||
|
|
||||||
|
* Immutable Object
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
|
|
||||||
Provide objects which follow value semantics rather than reference semantics.
|
To create immutable objects that represent a descriptive aspect of the domain with no conceptual identity.
|
||||||
This means value objects' equality is not based on identity. Two value objects are
|
|
||||||
equal when they have the same value, not necessarily being the same object.
|
|
||||||
|
|
||||||
## Explanation
|
## Explanation
|
||||||
|
|
||||||
Real-world example
|
Real-world example
|
||||||
|
|
||||||
> There is a class for hero statistics in a role-playing game. The statistics contain attributes
|
> Consider the case of a business card. In the real world, a business card contains information such as the person's name, job title, phone number, and email address. This information represents a specific and complete set of attributes describing the contact details of an individual but doesn't have an identity itself beyond this information.
|
||||||
> such as strength, intelligence, and luck. The statistics of different heroes should be equal
|
>
|
||||||
> when all the attributes are equal.
|
> In a software system, you can create a `BusinessCard` class as a Value Object. This class would be immutable, meaning once a `BusinessCard` object is created with a person's details, those details cannot change. If you need a different business card, you create a new instance rather than modifying the existing one. The equality of two `BusinessCard` objects would be based on their contained data rather than their memory addresses, ensuring that two business cards with the same details are considered equal. This mirrors how business cards in real life are used and compared based on their content, not on the physical card itself.
|
||||||
|
|
||||||
In plain words
|
In plain words
|
||||||
|
|
||||||
> Value objects are equal when their attributes have the same value
|
> Value objects are equal when their attributes have the same value.
|
||||||
|
|
||||||
Wikipedia says
|
Wikipedia says
|
||||||
|
|
||||||
> In computer science, a value object is a small object that represents a simple entity whose
|
> In computer science, a value object is a small object that represents a simple entity whose equality is not based on identity: i.e. two value objects are equal when they have the same value, not necessarily being the same object.
|
||||||
> equality is not based on identity: i.e. two value objects are equal when they have the same
|
|
||||||
> value, not necessarily being the same object.
|
|
||||||
|
|
||||||
**Programmatic Example**
|
**Programmatic Example**
|
||||||
|
|
||||||
Here is the `HeroStat` class that is the value object. Notice the use of
|
There is a class for hero statistics in a role-playing game. The statistics contain attributes such as strength, intelligence, and luck. The statistics of different heroes should be equal when all the attributes are equal.
|
||||||
[Lombok's `@Value`](https://projectlombok.org/features/Value) annotation.
|
|
||||||
|
Here is the `HeroStat` class that is the value object. Notice the use of [Lombok's `@Value`](https://projectlombok.org/features/Value) annotation.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@Value(staticConstructor = "valueOf")
|
@Value(staticConstructor = "valueOf")
|
||||||
|
@ToString
|
||||||
class HeroStat {
|
class HeroStat {
|
||||||
|
|
||||||
int strength;
|
int strength;
|
||||||
@@ -52,12 +57,12 @@ var statA = HeroStat.valueOf(10, 5, 0);
|
|||||||
var statB = HeroStat.valueOf(10, 5, 0);
|
var statB = HeroStat.valueOf(10, 5, 0);
|
||||||
var statC = HeroStat.valueOf(5, 1, 8);
|
var statC = HeroStat.valueOf(5, 1, 8);
|
||||||
|
|
||||||
LOGGER.info(statA.toString());
|
LOGGER.info("statA: {}", statA);
|
||||||
LOGGER.info(statB.toString());
|
LOGGER.info("statB: {}", statB);
|
||||||
LOGGER.info(statC.toString());
|
LOGGER.info("statC: {}", statC);
|
||||||
|
|
||||||
LOGGER.info("Is statA and statB equal : {}", statA.equals(statB));
|
LOGGER.info("Are statA and statB equal? {}", statA.equals(statB));
|
||||||
LOGGER.info("Is statA and statC equal : {}", statA.equals(statC));
|
LOGGER.info("Are statA and statC equal? {}", statA.equals(statC));
|
||||||
```
|
```
|
||||||
|
|
||||||
Here's the console output.
|
Here's the console output.
|
||||||
@@ -72,24 +77,49 @@ Here's the console output.
|
|||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Applicability
|
## Applicability
|
||||||
|
|
||||||
Use the Value Object when
|
Use the Value Object when
|
||||||
|
|
||||||
* The object's equality needs to be based on the object's value
|
* 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.
|
||||||
|
|
||||||
## Known uses
|
## Known uses
|
||||||
|
|
||||||
|
* Implementing complex data types like monetary values, measurements, and other domain-specific values.
|
||||||
* [java.util.Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html)
|
* [java.util.Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html)
|
||||||
* [java.time.LocalDate](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html)
|
* [java.time.LocalDate](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html)
|
||||||
|
* [java.awt.Color](https://docs.oracle.com/javase/8/docs/api/java/awt/Color.html)
|
||||||
* [joda-time, money, beans](http://www.joda.org/)
|
* [joda-time, money, beans](http://www.joda.org/)
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Benefits:
|
||||||
|
|
||||||
|
* Simplifies code by making objects immutable.
|
||||||
|
* Thread-safe as the object's state cannot change after creation.
|
||||||
|
* Easier to reason about and maintain.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Related Patterns
|
||||||
|
|
||||||
|
* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): Often used to create instances of value objects.
|
||||||
|
* [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.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
* [Patterns of Enterprise Application Architecture](http://www.martinfowler.com/books/eaa.html)
|
* [Domain-Driven Design: Tackling Complexity in the Heart of Software](https://amzn.to/3wlDrze)
|
||||||
* [ValueObject](https://martinfowler.com/bliki/ValueObject.html)
|
* [Effective Java](https://amzn.to/4cGk2Jz)
|
||||||
* [VALJOs - Value Java Objects : Stephen Colebourne's blog](http://blog.joda.org/2014/03/valjos-value-java-objects.html)
|
* [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)
|
* [Value Object : Wikipedia](https://en.wikipedia.org/wiki/Value_object)
|
||||||
* [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)
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
/**
|
/**
|
||||||
* A Value Object are objects which follow value semantics rather than reference semantics. This
|
* A Value Object are objects which follow value semantics rather than reference semantics. This
|
||||||
* means value objects' equality are not based on identity. Two value objects are equal when they
|
* means value objects' equality are not based on identity. Two value objects are equal when they
|
||||||
* have the same value, not necessarily being the same object..
|
* have the same value, not necessarily being the same object.
|
||||||
*
|
*
|
||||||
* <p>Value Objects must override equals(), hashCode() to check the equality with values. Value
|
* <p>Value Objects must override equals(), hashCode() to check the equality with values. Value
|
||||||
* Objects should be immutable so declare members final. Obtain instances by static factory methods.
|
* Objects should be immutable so declare members final. Obtain instances by static factory methods.
|
||||||
@@ -51,11 +51,11 @@ public class App {
|
|||||||
var statB = HeroStat.valueOf(10, 5, 0);
|
var statB = HeroStat.valueOf(10, 5, 0);
|
||||||
var statC = HeroStat.valueOf(5, 1, 8);
|
var statC = HeroStat.valueOf(5, 1, 8);
|
||||||
|
|
||||||
LOGGER.info(statA.toString());
|
LOGGER.info("statA: {}", statA);
|
||||||
LOGGER.info(statB.toString());
|
LOGGER.info("statB: {}", statB);
|
||||||
LOGGER.info(statC.toString());
|
LOGGER.info("statC: {}", statC);
|
||||||
|
|
||||||
LOGGER.info("Is statA and statB equal : {}", statA.equals(statB));
|
LOGGER.info("Are statA and statB equal? {}", statA.equals(statB));
|
||||||
LOGGER.info("Is statA and statC equal : {}", statA.equals(statC));
|
LOGGER.info("Are statA and statC equal? {}", statA.equals(statC));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.value.object;
|
package com.iluwatar.value.object;
|
||||||
|
|
||||||
|
import lombok.ToString;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,6 +35,7 @@ import lombok.Value;
|
|||||||
* </a>
|
* </a>
|
||||||
*/
|
*/
|
||||||
@Value(staticConstructor = "valueOf")
|
@Value(staticConstructor = "valueOf")
|
||||||
|
@ToString
|
||||||
class HeroStat {
|
class HeroStat {
|
||||||
|
|
||||||
int strength;
|
int strength;
|
||||||
|
|||||||
Reference in New Issue
Block a user