Repository: iluwatar/java-design-patterns
Issue: Value Object pattern
Labels: epic: pattern

http://martinfowler.com/bliki/ValueObject.html


Issue comments:
I think the definition of the Value Object is fairly comprehensive.
Thinking of implementing a pattern. How about this : VALJO - Value Java Objects
which has more strict set of rules for a correctly defined immutable value object. 
Suggested by Stephen Colebourne.
http://blog.joda.org/2014/03/valjos-value-java-objects.html
If this is not bad I want to work on this.

This is a good specification. I think we're still talking about the same Value Object pattern even though the author mr. Colebourne invents this new term VALJO. So my suggestion would be to implement the Value Object pattern using the VALJO specs. Does this sound reasonable?

:+1:  Yes it is. I will get these details in the code and documentation. Can I work on this?

@JuhoKang Sure go ahead. :+1: I will update the labels.


Pull request review comments:
Did you mean to change this file?

Did you mean to change this?

Please describe the pattern and the example here. For reference, see other pattern examples.

I don't know why these blanks exist and make differences in this file. I only added one line <module>value-object</module>.


Patch that resolved the issue:
diff --git a/pom.xml b/pom.xml
index 482110e64d06..c15ca20cfb1c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -120,6 +120,7 @@
 		<module>delegation</module>
 		<module>event-driven-architecture</module>
 		<module>feature-toggle</module>
+		<module>value-object</module>
 	</modules>
 
 	<dependencyManagement>
@@ -199,8 +200,8 @@
 	<build>
 		<pluginManagement>
 			<plugins>
-				<!-- This plugin's configuration is used to store Eclipse m2e settings 
-					only. It has no influence on the Maven build itself. TODO: Remove when the 
+				<!-- This plugin's configuration is used to store Eclipse m2e settings
+					only. It has no influence on the Maven build itself. TODO: Remove when the
 					m2e plugin can correctly bind to Maven lifecycle -->
 				<plugin>
 					<groupId>org.eclipse.m2e</groupId>
@@ -256,10 +257,10 @@
 				<groupId>org.jacoco</groupId>
 				<artifactId>jacoco-maven-plugin</artifactId>
 				<version>${jacoco.version}</version>
-				<!-- The following exclude configuration was added because error occurred 
+				<!-- The following exclude configuration was added because error occurred
 					when executing "mvn clean test jacoco:report coveralls:report" -->
-				<!-- [ERROR] Failed to execute goal org.eluder.coveralls:coveralls-maven-plugin:3.1.0:report 
-					(default-cli) on project java-design-patterns: I/O operation failed: No source 
+				<!-- [ERROR] Failed to execute goal org.eluder.coveralls:coveralls-maven-plugin:3.1.0:report
+					(default-cli) on project java-design-patterns: I/O operation failed: No source
 					found for domainapp/dom/modules/simple/QSimpleObject.java -> [Help 1] -->
 				<configuration>
 					<excludes>
@@ -354,7 +355,7 @@
 					  <excludeFromFailureFile>exclude-pmd.properties</excludeFromFailureFile>
 				  </configuration>
                 </execution>
-              </executions>              
+              </executions>
             </plugin>
 
 			<plugin>
@@ -390,5 +391,5 @@
       </plugin>
     </plugins>
   </reporting>
-  
+
 </project>
diff --git a/value-object/etc/value-object.ucls b/value-object/etc/value-object.ucls
new file mode 100644
index 000000000000..7bbf5e47cfbd
--- /dev/null
+++ b/value-object/etc/value-object.ucls
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<class-diagram version="1.1.9" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true" 
+  realizations="true" associations="true" dependencies="true" nesting-relationships="true" router="FAN">  
+  <class id="1" language="java" name="com.iluwatar.value.object.HeroStat" project="value-object" 
+    file="/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java" binary="false" corner="BOTTOM_RIGHT">    
+    <position height="-1" width="-1" x="520" y="337"/>    
+    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
+      sort-features="false" accessors="true" visibility="true">      
+      <attributes public="true" package="true" protected="true" private="true" static="true"/>      
+      <operations public="true" package="true" protected="true" private="true" static="true"/>    
+    </display>  
+  </class>  
+  <classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
+    sort-features="false" accessors="true" visibility="true">    
+    <attributes public="true" package="true" protected="true" private="true" static="true"/>    
+    <operations public="true" package="true" protected="true" private="true" static="true"/>  
+  </classifier-display>  
+  <association-display labels="true" multiplicity="true"/>
+</class-diagram>
\ No newline at end of file
diff --git a/value-object/index.md b/value-object/index.md
new file mode 100644
index 000000000000..83223d8a2377
--- /dev/null
+++ b/value-object/index.md
@@ -0,0 +1,34 @@
+---
+layout: pattern
+title: Value Object
+folder: value-object
+permalink: /patterns/value-object/
+categories: Creational
+tags:
+ - Java
+ - Difficulty-Beginner
+---
+
+## Intent
+Provide 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 have the same value, not necessarily being the same object.
+
+![alt text](./etc/value-object.png "Value Object")
+
+## Applicability
+Use the Value Object when
+
+* you need to measure the objects' equality based on the objects' value
+
+## Real world examples
+
+* [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)
+* [joda-time, money, beans](http://www.joda.org/)
+
+## Credits
+
+* [Patterns of Enterprise Application Architecture](http://www.martinfowler.com/books/eaa.html)
+* [VALJOs - Value Java Objects : Stephen Colebourne's blog](http://blog.joda.org/2014/03/valjos-value-java-objects.html)
+* [Value Object : Wikipedia](https://en.wikipedia.org/wiki/Value_object)
diff --git a/value-object/pom.xml b/value-object/pom.xml
new file mode 100644
index 000000000000..1ed2adb4377c
--- /dev/null
+++ b/value-object/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<project
+	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" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.iluwatar</groupId>
+		<artifactId>java-design-patterns</artifactId>
+		<version>1.11.0-SNAPSHOT</version>
+	</parent>
+	<artifactId>value-object</artifactId>
+	<dependencies>
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava-testlib</artifactId>
+			<version>19.0</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.mockito</groupId>
+			<artifactId>mockito-core</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>
diff --git a/value-object/src/main/java/com/iluwatar/value/object/App.java b/value-object/src/main/java/com/iluwatar/value/object/App.java
new file mode 100644
index 000000000000..85779fcb78ac
--- /dev/null
+++ b/value-object/src/main/java/com/iluwatar/value/object/App.java
@@ -0,0 +1,32 @@
+package com.iluwatar.value.object;
+
+/**
+ * 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
+ * have the same value, not necessarily being the same object..
+ * 
+ * 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.
+ * The elements of the state must be other values, including primitive types. 
+ * Provide methods, typically simple getters, to get the elements of the state.
+ * A Value Object must check equality with equals() not == 
+ * 
+ * For more specific and strict rules to implement value objects check the rules from Stephen
+ * Colebourne's term VALJO : http://blog.joda.org/2014/03/valjos-value-java-objects.html 
+ */
+public class App {
+  /**
+   * This practice creates three HeroStats(Value object) and checks equality between those.
+   */
+  public static void main(String[] args) {
+    HeroStat statA = HeroStat.valueOf(10, 5, 0);
+    HeroStat statB = HeroStat.valueOf(10, 5, 0);
+    HeroStat statC = HeroStat.valueOf(5, 1, 8);
+
+    System.out.println(statA.toString());
+
+    System.out.println("Is statA and statB equal : " + statA.equals(statB));
+    System.out.println("Is statA and statC equal : " + statA.equals(statC));
+  }
+}
diff --git a/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java b/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java
new file mode 100644
index 000000000000..101837db068b
--- /dev/null
+++ b/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java
@@ -0,0 +1,90 @@
+package com.iluwatar.value.object;
+
+/**
+ * HeroStat is a value object
+ * 
+ * {@link http://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html}
+ */
+public class HeroStat {
+
+  // Stats for a hero
+
+  private final int strength;
+  private final int intelligence;
+  private final int luck;
+
+  // All constructors must be private.
+  private HeroStat(int strength, int intelligence, int luck) {
+    super();
+    this.strength = strength;
+    this.intelligence = intelligence;
+    this.luck = luck;
+  }
+
+  // Static factory method to create new instances.
+  public static HeroStat valueOf(int strength, int intelligence, int luck) {
+    return new HeroStat(strength, intelligence, luck);
+  }
+
+  public int getStrength() {
+    return strength;
+  }
+
+  public int getIntelligence() {
+    return intelligence;
+  }
+
+  public int getLuck() {
+    return luck;
+  }
+
+  /*
+   * Recommended to provide a static factory method capable of creating an instance from the formal
+   * string representation declared like this. public static HeroStat parse(String string) {}
+   */
+
+  // toString, hashCode, equals
+
+  @Override
+  public String toString() {
+    return "HeroStat [strength=" + strength + ", intelligence=" + intelligence
+        + ", luck=" + luck + "]";
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + intelligence;
+    result = prime * result + luck;
+    result = prime * result + strength;
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null) {
+      return false;
+    }
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+    HeroStat other = (HeroStat) obj;
+    if (intelligence != other.intelligence) {
+      return false;
+    }
+    if (luck != other.luck) {
+      return false;
+    }
+    if (strength != other.strength) {
+      return false;
+    }
+    return true;
+  }
+
+  // The clone() method should not be public. Just don't override it.
+
+}


Test patch:
diff --git a/value-object/src/test/java/com/iluwatar/value/object/AppTest.java b/value-object/src/test/java/com/iluwatar/value/object/AppTest.java
new file mode 100644
index 000000000000..aed3c2f20511
--- /dev/null
+++ b/value-object/src/test/java/com/iluwatar/value/object/AppTest.java
@@ -0,0 +1,15 @@
+package com.iluwatar.value.object;
+
+import org.junit.Test;
+
+/**
+ * Application test
+ */
+public class AppTest {
+
+  @Test
+  public void test() {
+    String[] args = {};
+    App.main(args);
+  }
+}
diff --git a/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java b/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java
new file mode 100644
index 000000000000..785a4d8fefb0
--- /dev/null
+++ b/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java
@@ -0,0 +1,44 @@
+package com.iluwatar.value.object;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+
+import static org.junit.Assert.assertThat;
+
+import com.google.common.testing.EqualsTester;
+
+import org.junit.Test;
+
+/**
+ * Unit test for HeroStat.
+ */
+public class HeroStatTest {
+
+  /**
+   * Tester for equals() and hashCode() methods of a class. Using guava's EqualsTester.
+   * 
+   * @see http://static.javadoc.io/com.google.guava/guava-testlib/19.0/com/google/common/testing/
+   *      EqualsTester.html
+   */
+  @Test
+  public void testEquals() {
+    HeroStat heroStatA = HeroStat.valueOf(3, 9, 2);
+    HeroStat heroStatB = HeroStat.valueOf(3, 9, 2);
+    new EqualsTester().addEqualityGroup(heroStatA, heroStatB).testEquals();
+  }
+
+  /**
+   * The toString() for two equal values must be the same. For two non-equal values it must be
+   * different.
+   */
+  @Test
+  public void testToString() {
+    HeroStat heroStatA = HeroStat.valueOf(3, 9, 2);
+    HeroStat heroStatB = HeroStat.valueOf(3, 9, 2);
+    HeroStat heroStatC = HeroStat.valueOf(3, 9, 8);
+
+    assertThat(heroStatA.toString(), is(heroStatB.toString()));
+    assertThat(heroStatA.toString(), is(not(heroStatC.toString())));
+  }
+
+}