1. 개요
우리는 종종 원시 값을 포장한 Value Object를 볼 수 있습니다. Value Object는 원시 값을 클래스로 포장하여 의미를 명확히 하고, 해당 값의 유효성을 검증할 수 있는 장점을 제공합니다.
2. 내부 검증
원시 값 포장을 통해서 우리는 값을 더욱 더 안전하게 사용할 수 있습니다. Value Object는 객체 생성 시 값을 검증하여 잘못된 값이 사용되는 것을 방지할 수 있습니다. 예를 들어, 나이를 나타내는 값을 포장한 객체를 생각해봅시다.
public class Age {
private final int value;
public Age(int value) {
if (value < 0 || value > 150) {
throw new IllegalArgumentException("나이는 0 이상 150 이하이어야 합니다.");
}
this.value = value;
}
public int getValue() {
return value;
}
}
Java
복사
위 예제에서 Age 클래스는 나이 값이 유효한지 검증하고, 잘못된 값이 들어올 경우 예외를 던집니다. 이렇게 함으로써 나이를 사용할 때 항상 유효한 값만 사용하도록 보장할 수 있습니다.
3. 진정한 객체 지향
다음 코드를 함께 봅시다.
class Car {
private String driver;
private int position;
public void accelerate() {
this.position++;
}
}
Java
복사
위 클래스는 단순히 driver라는 이름의 String 값과 position이라는 이름의 int 값을 가지고 있는 클래스일 뿐입니다. 이 클래스는 단순히 데이터를 저장하고 몇 가지 동작을 수행할 수 있지만, 진정한 객체 지향의 이점을 충분히 활용하지는 못하고 있습니다.
우리가 진정한 객체 지향 설계를 사용하여 Car 클래스를 개선해봅시다. 예를 들어, driver와 position을 별도의 Value Object로 분리하고, 각 Value Object가 자신의 유효성을 검증하도록 할 수 있습니다.
class Car {
private Driver driver;
private Position position;
public Car(Driver driver, Position position) {
this.driver = driver;
this.position = position;
}
public void accelerate() {
this.position = this.position.increment();
}
public String getDriverName() {
return this.driver.getName();
}
public int getPosition() {
return this.position.getValue();
}
}
class Driver {
private final String name;
public Driver(String name) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("운전자의 이름은 비어있을 수 없습니다.");
}
this.name = name;
}
public String getName() {
return name;
}
}
class Position {
private final int value;
public Position(int value) {
if (value < 0) {
throw new IllegalArgumentException("위치는 음수일 수 없습니다.");
}
this.value = value;
}
public Position increment() {
return new Position(this.value + 1);
}
public int getValue() {
return value;
}
}
Java
복사
위 예제에서 Car 클래스는 Driver와 Position이라는 Value Object를 가지고 있습니다. Driver와 Position은 각각 자신의 유효성을 검증하며, Car 클래스는 이 Value Object들을 조합하여 동작합니다. 이렇게 함으로써 각 클래스의 책임이 명확해지고, 코드의 재사용성과 유지보수성이 향상됩니다.