[JAVA] From the habit of creating Value Objects for object-oriented understanding

Introduction

In the last year or so, I've finally been able to write object-oriented code. I first learned about object-orientation in 1995, so it took me more than 20 years.

The reason why I became able to write object-oriented code This is because I tried to write with the Value Object in mind.

What is a Value Object?

The simplest definition is the translation of the article written by Dr. Martin Fowler (Value Object) [^ 1].

Implementation example in Java

In the case of Java, it is good to satisfy the following two. It's not directly related, but it also implements toString ().

  1. Implement equals () and hashCode ()
  2. All fields are final [^ 2]

For example, let's implement a [java.time.Year] [] -like class, MyYear. Since it is troublesome to implement equals () and hashCode () properly, I leave it to the IDE (IntelliJ IDEA).

public final class MyYear {
    private final int year;

    private MyYear(int year) {
        this.year = year;
    }

    public static MyYear of(int year) {
        return new MyYear(year);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        MyYear myYear = (MyYear) o;

        return year == myYear.year;
    }

    @Override
    public int hashCode() {
        return year;
    }

    @Override
    public String toString() {
        return "MyYear{" +
                "year=" + year +
                '}';
    }
}

You can also use Apache Commons Lang for your implementation. It uses reflection, but the description is simple. (Some APIs do not use reflection)

The reason why SHORT_PREFIX_STYLE is used for toString () is You don't need the hash code for the object, you only need the value of the field.

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

public final class MyYear {
    private final int year;

    private MyYear(int year) {
        this.year = year;
    }

    public static MyYear of(int year) {
        return new MyYear(year);
    }

    @Override
    public boolean equals(Object o) {
        return EqualsBuilder.reflectionEquals(this, o);
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

merit

For the time being, there are three major benefits.

Easy to aggregate logic

For example, if you want a method that returns next year, you can implement it in the MyYear method. The logic is concentrated in one class, making it easy to change.

public MyYear nextYear() {
    return new MyYear(this.year + 1);
}

Can be used as a map key

It can be used as a key as follows.

import java.util.HashMap;

public final class Main {
    public static void main(String[] args) {
        var myMap = new HashMap<>();
        myMap.put(MyYear.of(2018), "2018");
        System.out.println(myMap.get(MyYear.of(2018))); // =>2018. equals()Null if there is no
    }
}

Easy to write tests

You can compare objects normally. If you don't define equals (), you'll have to define a getter to get the value of the field.

import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

public final class MyYearTest {
    @Test
    public void testMyYearNext() {
        MyYear now = MyYear.of(2018);
        MyYear next = MyYear.of(2019);

        assertThat(now.nextYear()).isEqualTo(next);
    }
}

Why Value Object

Object-oriented definitions often include "encapsulation," "inheritance," and "polymorphism." Aside from polymorphism, the other two don't quite fit.

Encapsulation is not a setter / getter, but I couldn't sort out what to write, whether inheritance is really necessary, and so on.

However, since I started to create Value Objects properly, I have less hesitation and can write code that is easy to understand. The only weakness is "it's a pain to write at first", but I think this is a necessary cost.

in conclusion

Actually, I think that Domain Driven Design may be easier to understand than Object Oriented, but I thought it was too outlandish, so I focused on Value Object for the time being.

If you want to do it more seriously (?), The following essay, which is also called "OO code training cast", will be helpful. However, this is pretty tough. I can't ... (ヽ ´ω`)

[^ 1]: Various additions have been made to the original text (ValueObject), but this time it is just a definition, so I will use the old description. [^ 2]: Even if final is added, it cannot be said that it is immutable if List etc. is included, but the explanation is omitted here. [java.time.Year]: https://docs.oracle.com/javase/jp/8/docs/api/java/time/Year.html

Recommended Posts

From the habit of creating Value Objects for object-oriented understanding
Extract a specific element from the list of objects
Reintroduction to Java for Humanities 0: Understanding the Act of Programming
Aiming for a basic understanding of the flow of recursive processing
The story of switching from Amazon RDS for MySQL to Amazon Aurora Serverless
[Challenge CircleCI from 0] Learn the basics of CircleCI
Samshin on the value of the hidden field
The process of understanding Gemfile by non-engineers
The story of RxJava suffering from NoSuchElementException
Ruby from the perspective of other languages
WebMvcConfigurer Memorandum of Understanding for Spring Boot 2.0 (Spring 5)
Easily measure the size of Java Objects
Deepened my understanding of the merge method
Find the difference from a multiple of 10
[Rails] Set validation for the search function using Rakuten API (from the implementation of Rakuten API)