💭 개요
Vo, Dto, Dao, Entity 등등 여러가지의 객체 중에 Vo에 대해 알아보자
💡 공부한 것
VO
Value Object의 약자로 문자 그대로 ‘값 객체’를 뜻한다. 좀 더 쉽게 풀어 이야기 하면, 객체를 ‘값’처럼 취급한다.
💡 Vo의 목적은 신뢰할 수 있고, 예측 가능한 객체를 만드는 것이다.
일정한 조건 아래에서 일관되게 동작하고, 예측한 대로 결과를 제공한다는 것이 목표이다.
불변성
말 그대로 ‘변하지 않는다는 뜻’이다. 자바에서는 값을 변하지 않게 하고 싶을 때 예약어 final을 사용한다.
final로 선언된 멤버 변수는 한번 선언되면 변하지 않는다.
모든 멤버 변수가 final 선언이 되어 있으면 반드시 Vo 일까? 몇가지 확인해야될 부분이 있다.
멤버 변수가 원시 타입이 아닌 참조 타입으로 된 객체가 있다면 불변성이 보장되지 않을 수 있다.
public class Person{
private final name;
private final List<String> hobbies;
public Person(String name, List<String> hobbies) {
this.name = name;
this.hobbies = hobbies; // 참조 타입을 직접 할당
}
public String getName(){
return name;
}
public List<String> getHobbies(){
return hobbies; // 참조 타입 반환
}
}
public class Main {
publi static void maing(String[] args){
List<String> hobbies = new ArrayList<>();
hobbies.add("swimming")
hobbies.add("running")
Person person = new Person("burningMan", hobbies);
// 반환된 리스트를 수정
person.getHobbies().add("study");
}
}
생성자에서 초기화 된 이후에도 불변성을 보장하지 않는다. Person 객체의 내부 상태가 외부에 의해 변경 될 수 있다. 이를 막기 위해 ‘방어적 복사’를 사용할 수 있다.
class Person {
private final String name;
private final List<String> hobbies;
public Person(String name, List<String> hobbies) {
this.name = name;
// 새로운 리스트로 복사하여 불변성 보장
this.hobbies = new ArrayList<>(hobbies);
}
public String getName() {
return name;
}
public List<String> getHobbies() {
// 반환 시에도 새롭게 생성한 객체를 반환하여 외부에서 멤버 변수가 직접적으로 변경되지 않도록 함
return new ArrayList<>(hobbies);
}
}
또한 멤버변수로 참조한 다른 객체가 불변이 아닐 수도 있고, 상속 받은 부모 클래스의 멤버 변수가 불변이 아닐수도 있다.
동등성
값은 내재된 의미가 같다면 같은 것이다.
public class Person{
private final String name;
private final int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
}
Person person1 = new Person("park",16);
Person person2 = new Person("park",16);
System.out.println(person1 == persion2) // true? false?
같은 값을 넣고 생성한 두 객체를 == 로 비교한다면 어떤 값이 나올까?
==는 메모리 주소(참조)를 비교하므로 답은 false이다. 이는 Vo의 목표와 맞지 않는다.
따라서 자바에서는 객체 간 비교에 사용되는 equals와 hashcode를 오버라이딩해서 상태를 비교하는 코드로 바꿔야 한다.
롬복을 사용한다면 @Value 어노테이션을 사용해도 좋다.
- equals와 hashcode 메서드가 객체의 상태에 따라 비교하는 메서드로 자동 생성된다.
- 멤버 변수가 final로 선언된다.
- 클래스가 final로 선언 된다.
- 해당 객체가 Vo라는걸 명시적으로 나타낼 수 있다.
자가 검증
클래스 스스로 상태가 유효한지 검증할 수 있음을 의미한다. 자체적으로 객체를 생성할 때, 검증을 통해 유효한 상태의 객체만 만들 수 있도록한다. 이 말은 한번 생성한 객체의 상태는 신뢰할 수 있다는 말이 된다.
아무리 객체가 불변이고 동등하다고 해도, 값 자체가 잘못 들어가버리면 해당 객체는 신뢰할 수 없다. Vo는 자가검증을 하기 때문에 이 객체를 사용할 때 혹시나 예측 불가능하게 작동할지 걱정하지 않아도 된다.
public class Person{
private final String name;
private final int age;
public Person(String name, int age){
if(name == null }} name.trim().isEmpty()){
throw new IllegalArgumentException("Name cannot be a null or empty.");
}
if(age < 0) {
throw new IllegalArgumentException("Age cannot be negative"
}
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
VO의 목적 생각하기
불변성, 동등성, 자가검증 등 Vo의 특징들은 Vo의 목표를 이루기 위한 개념일뿐이다.
결국 이러한 특징들은 신뢰성 있고, 예측가능한 객체를 만들기 위해서 필요한 것들을 갖추다보니 생겨난 것들이다. 신뢰할 수 있는 객체는 어떻게 만들지, 어떤 값을 불변으로 만들지, 어디까지 값을 보장해 할지 고민을 해야한다.
Vo 라는 이름에 휘둘리기 보다는 불변성, 동등성, 자가검증, 신뢰할 수 있는 객체를 추구해야한다.
🔠 Keyword 정리
Value Object, 불변성, 동등성, 자가 검증, 방어적 복사, equals, hashcode, @Value,
'Book > 자바,스프링 개발자를 위한 실용주의 프로그래밍' 카테고리의 다른 글
| 04. SOLID 원칙 (유지보수성과 확장성을 높이자.) (1) | 2024.09.11 |
|---|---|
| 03. 행동 ( 행동과 역할, 인터페이스 .. ) (0) | 2024.09.09 |
| 2.2 객체의 종류 (DTO,DAO,Entity ..) (0) | 2024.09.04 |
| 1. 객체 지향 프로그래밍이란? (순차, 절차 지향과의 비교..) (0) | 2024.08.31 |