Egloos | Log-in


JavaBeans와 Value Object

  • Value Object는 상태가 변하지 않기 때문에 몇가지 장점이 있다. 자바빈즈(JavaBeans) 규약을 따르는 iBatis 등은 외부에서 읽은 데이터를setter를 통해 객체에 넣어준다. 외부에서 데이터를 읽을 때만 setter를 제공하고 그 외에는 Value Object로 사용할 수 있으면 좋겠다.

1. 짧게 말하면

ValueObject 패턴에 따라 상태가 변하지 않는 Immutable 클래스를 구현하고, 이를 상속 받아 setter를 추가한Mutable 클래스를 구현하여 setter가 필요한 API에서는 Mutable 클래스를 사용하고 그 외에는 Immutable클래스를 사용한다.

2. 설명하자면

보통 자바빈즈 규약을 따르는 클래스는 다음과 같이 구현한다.

public class Person extends ObjectSupport {
private String name;
private int age;

public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public void older() { age += 1; }
}

자바빈즈 규약을 따르는 API들로 name, age값을 읽거나 쓸 수 있다. 외부에서 객체를 읽을 때 말고는 setter를 이용해값을 바꾸는 경우는 드물지만 자바빈즈 규약을 따르는 API를 사용해야 한다면 어쩔 수 없이 setter를 제공해야 한다. 객체를어떤 메서드의 파라메터로 전달 했을 때 setter가 제공되기 때문에 어디서나 예기치 않게 값이 바뀔 수도 있다. 예기치 않게값이 바뀌는 것을 원하지 않지만 자바빈즈 규약을 따르는 API에는 setter를 제공해야 한다. 어떻게 해야할까?

책임을 분리하면 된다. Immutable 인터페이스와 Mutable 인터페이스를 분리해서 제공한다.

public class Person extends ObjectSupport {
protected String name;
protected int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
public Person older() {
return age > 100
? new DeadPerson(name, age)
: new Mutable(this).setAge(getAge() + 1);
}

public static class Mutable extends Person {
public Mutable(Person prototype) { this(prototype.name, prototype.age); }
public Mutable() { }
public Mutable setName(String name) { this.name = name; return this; }
public Mutable setAge(int age) { this.age = age; return this; }
}
}

일반적인 경우는 다음과 같이 사용한다.

Person one = new Person("Eung-ju PARK", 29);
Person olderOne = one.older();
...

iBatis에서는 다음과 같이 ResultMap을 정의하고 DAO를 구현한다.

<resultMap id="result" class="package.Person$Mutable">
<result property="name" column="name"/>
<result property="age" column="age"/>
</resultMap>

public Person find(String name) {
return (Person)getSqlMapClientTemplate().queryForObject("Person.find", name);
}

이제 한 번 만들어진 객체는 값이 변경되지 않는다! 멀티 쓰레드 환경에서도 캐시를 써도 이제 전혀 불안하지 않다. 혹Mutable로 업캐스팅 해서 몰래 값을 바꾼다고 해도 Mutable을 참조하는 곳을 찾으면 어디서 바꾸는지 쉽게 제거할 수있다.

3. 참고

이 글과 관련있는 글을 자동검색한 결과입니다 [?]

by 이피 | 2008/07/14 01:03 | Java | 트랙백(1) | 덧글(4)

트랙백 주소 : http://colus.egloos.com/tb/4486166
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Tracked from IT's Lives. at 2008/07/29 21:08

제목 : Value Object : 무슨 짓을 하던지 상태값..
http://colus.egloos.com/4486166 를 보고.. 밸류 오브젝트(Value Object)는 상태값을 가지는 객체이다. 가장 손쉬운 예가 numbers, dates, monies등이라고 할 수 있겠다. DAO 패턴을 사용하면서 가장 많이 고민꺼리는 밸류 오브젝트는 어느정도의 로직을 가질 수 있는 것인가? 이었다. 이피님의 말로써는 Value Object는 무슨 짓을 하든 내부 상태만 바꾸지 않으면 Value Object입니다.......more

Commented by 정재훈 at 2008/07/14 12:22
아이디어 좋습니다.^^
Commented by 허혁 at 2008/07/18 12:09
Method Chainging과는 정말 잘 붙는 것 같습니다.

TDD책의 Money 구현도 연상되네요..

그런데 older 메소드를 보면

Value 객체에 로직이 들어가잖아요..

validate 메소드 같은 것도 들어가는 것은 어떤가요?

어느 정도 로직이 들어가야 Value Object의 의미가 깨지지 않는다고 생각하세요?
Commented by 이피 at 2008/07/18 13:37
ProtocolBuffer는 Message.Builder에 build 메서드가 있습니다. setXyz로 값을 넣어주고 build할 때 검사를 하고 제대로 만들었으면 객체를 돌려주는 것 같더군요. 이 방법이 좋은 것 같아요.

Value Object는 무슨 짓을 하든 내부 상태만 바꾸지 않으면 Value Object입니다.
Commented by 이피 at 2008/07/18 13:39
setter 체이닝에 문제가 있는데 iBatis는 setter 리턴타입이 void가 아니라도 괜잖은데 Struts2는 꼭 void이어야 하더군요. 각기 처리방식이 다른 것 같아요.

:         :

:

비공개 덧글

◀ 이전 페이지          다음 페이지 ▶