김지팡의 저장소
728x90

 

해당 포스팅은 <자바/스프링 개발자를 위한 실용주의 프로그래밍>의 2번째 챕터인 ‘객체의 종류’를 읽고 요약한 글이며, 이전 포스팅에서 이어지는 글입니다.

 

자바/스프링 개발자를 위한 실용주의 프로그래밍 - 예스24

소프트웨어 개발을 잘하고 싶다면 ‘개발’ 공부를 해야 합니다!자바 개발자가 코틀린 같은 신생 언어를 다룰 수 있게 된다고 해서 개발을 더 잘하게 되는 것은 아니다. 소프트웨어 개발 능력을

m.yes24.com

 

 

CH 02 - 객체의 종류 | 값 객체(1)

해당 포스팅은 의 2번째 챕터인 ‘객체의 종류’를 읽고 요약한 글이다. 자바/스프링 개발자를 위한 실용주의 프로그래밍 - 예스24소프트웨어 개발을 잘하고 싶다면 ‘개발’ 공부를 해야 합니

happygimy97.tistory.com

 

 

🔗 동등성

동등성이란 서로 같은지를 뜻한다. 예시를 통해 이야기해 보겠다.

public class Color {
    public final int r;
    public final int g;
    public final int b;

    public Color(int r, int g, int b) {
        this.r = r;
        this.g = g;
        this.b = b;
    }
}


Color a = new Color(100, 100, 100);
Color b = new Color(100, 100, 100);

 

a와 b가 동등한 지를 물어본다면 2가지 의견이 나올 수 있을 것이다. Color 클래스 내에 선언된 변수(특성)들의 값이 같기에 a와 b는 동등하다는 의견과 a와 b의 참조값이 다르기 때문에 동등하지 않다는 의견이다. 다시 말하는 것이지만 객체지향 프로그래밍은 객체 간의 협력을 기반으로 설계를 하기에 신뢰성이 중요하다. 즉 불확실성이 존재하면 안 된다는 의미이다.

 

그렇기에 a와 b가 동등한 지에 대해서 정확히 예측하지 못한다는 점에서 분명 불확실성이 높다고 말할 수 있다고 생각한다.

VO(값 객체)에서는 내재된 값이 같으면 동등한 객체로 본다고 한다.

 

하지만 자바에서는 기본적으로 참조값이 다르면 다른 객체로 보기 때문에 이를 위해서는 equals() hashCode() 메서드를 각 객체의 내재된 값이 같으면 동등하다로 보도록 Override(재정의)할 필요가 있다.

 

여기까지 불변성과 동등성을 지키기 위해서 필요한 것들을 알아보았다. 스프링에서는 Lombok 라이브러리의 @Value 어노테이션을 클래스에 붙이면 쉽게 지킬 수 있게 해 준다. 즉 불변 객체를 생성하도록 도와준다는 것이다.

 

@Value
public class Person {
    private String name;
    private int age;
}
/*
위처럼 @Value 어노테이션을 붙이면 자동으로 생성자, getter, setter, toString(), equals(), hashCode()를 생성해주기에 아래 코드와 동일한 코드라고 볼 수 있다.
*/
public final class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person(name=" + name + ", age=" + age + ")";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

 

 

👨‍💻 식별자를 가진 클래스는 VO가 될 수 없습니다.
앞서 불변성에 대해 이야기할 때 예시로 나왔던 코드가 있다. 만약 해당 코드에 식별자인 id가 있다고 가정하겠다.

public class AccountInfo {
    public final long id;
    public final int grade;

    public AccountTier getTier() {
        if (grade > 100000) return "DIAMOND";
        else if (grade > 50000) return "GOLD";
        else if (grade > 30000) return "SILVER";
        else return "BRONZE";
    }

    public AccountInfo withGrade(int grade) {
        return new AccountInfo(this.id , grade);
    }
}

AccountInfo account1 = new AccountInfo(1, 20000);
AccountInfo account2 = account1.withGrade(40000);
System.out.println(account1 == account2); // false 출력

식별자의 정의에 따르면 id가 같기에 account1과 account2가 같아야 하지만, account2는 새로운 객체를 할당받은 것이기에 account1과 account2의 동등성을 비교하면 false가 나온다.

 

즉 동등성과 식별자는 의미상 충돌이 생길 수밖에 없다. 따라서 식별자를 가지고 있는 객체는 VO가 될 수 없는 것이다.

 

 

🔗 자가 검증

자가 검증은 스스로 클래스 내 특성들이 유효한 것인지를 검증할 수 있냐는 것을 의미한다. VO의 목표가 신뢰할 수 있는 객체가 되는 것이기에 VO를 사용하는 사람이 아니라 VO 자체적으로 자가 검증이 이루어져야만 한다.

 

자가 검증을 통해 외부에서 VO를 사용할 때 이상한 값이 들어가 있는 것을 아닐지에 대한 고려를 할 필요가 전혀 없어지게 된다. 그저 사용하면 된다.

 

 

 

🧑‍💻 마무리

프로젝트를 하면서 값 객체를 사용해 본 적은 아직까진 없지만 객체 간의 협력을 기반으로 동작하는 객체지향 프로그래밍에서 값 객체는 중요한 요소가 될 것이라는 생각이 든다.

728x90
profile

김지팡의 저장소

@김지팡

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!