태그 : 번역
2009/09/28 [번역] 제어 역전 컨테이너와 의존성 주입 패턴 (1/4)
2008/06/13 [번역] 도메인 로직과 SQL
2008/01/25 [번역] XStream 배우기 : 변환기(Converter) 배우기
2008/06/13 [번역] 도메인 로직과 SQL
2008/01/25 [번역] XStream 배우기 : 변환기(Converter) 배우기

# by | 2009/09/28 00:37 | 프로그래밍 이야기 | 트랙백 | 덧글(0)
원본 글은 http://martinfowler.com/articles/dblogic.html 에 있습니다.
최종 주요 갱신: 2003년 2월
우리는 지난 20여 년 이상 데이터베이스 지향 소프트웨어 개발자와 메모리에서 처리하는 애플리케이션 소프트웨어 개발자 간의 간격이 커지는 것을 봐왔습니다. 이 때문에 SQL이나 저장 프로시듀어 같은 데이터베 이스의 기능을 어떻게 이용할지에 대한 논의가 생겨났습니다. 이 글에서 전 업무 로직을 SQL 질의문 안에 둘 것인지 아니면 메모리에 있는 코드 안에 둘 것인지에 대한 문제를 주로 성능과 유지보수성의 관점에서 단순한 -그러나 SQL 충분히 활용한 - 예제를 사용해 살펴보려고 합니다.
# by | 2008/06/13 21:03 | 프로그래밍 이야기 | 트랙백(8) | 핑백(1) | 덧글(0)
package com.thoughtworks.xstream.examples;
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.thoughtworks.xstream.examples;매우 보기 싫은 결과가 나옵니다. XML 코드에 패키지명을 포함한 전체 클래스 이름이 들어 있습니다.
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class PersonTest {
public static void main(String[] args) {
Person person = new Person();
person.setName("Guilherme");
XStream xStream = new XStream(new DomDriver());
System.out.println(xStream.toXML(person));
}
}
<com.thoughtworks.xstream.examples.Person>이제 이 긴 클래스 이름을 보다 인간다운 뭔가(예를 들면 'person')로 바꾸는데 사용할 별칭(alias)를 만듭니다.
<name>Guilherme</name>
</com.thoughtworks.xstream.examples.Person>
XStream xStream = new XStream(new DomDriver());결과물이 한결 일기 쉽고 간결해졌습니다.
xStream.alias("person", Person.class);
System.out.println(xStream.toXML(person));
<person>앞으로 가지고 놀 간단한 클래스를 구성했습니다. XStream 변환기가 어떤 일을 해주는지 보기로 하겠습니다.
<name>Guilherme</name>
</person>
package com.thoughtworks.xstream.examples;이제 호출하면 Person 객체만 다룰 수 있다는 것을 얘기해줍니다. Person 외에는 전혀 다룰 수 없습니다. Person을 상속한 클래스를 포함해서......
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class PersonConverter implements Converter {
public boolean canConvert(Class clazz) {
return false;
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
return null;
}
}
public boolean canConvert(Class clazz) {
return clazz.equals(Person.class);
}generic 변환기를 작업하지 않는이상 두번째 단계는 분명합니다.Person person = (Person) value;이제 데이터를 출력할 수 있습니다. 'fullname'이라는 노드를 만드는 것으로 시작해서 person 객체의 name 값을 추가합니다.
writer.startNode("fullname");
writer.setValue(person.getName());
writer.endNode();간단하죠?public void marshal(Object value, HierarchicalStreamWriter writer,startNode()와 endNode()는 원하는 만큼 여러번 호출할 수 있습니다. 하지만 열어 놓은 것들은 모두 닫아야 한다는 것을 잊지 마십시오. 그리고 변환 작업은 대부분 setValue() 메소드를 호출하는 부분에서 합니다.
MarshallingContext context) {
Person person = (Person) value;
writer.startNode("fullname");
writer.setValue(person.getName());
writer.endNode();
}
Person person = new Person();다음과 같은 변환기가 주어졌습니다.
reader.moveDown();
person.setName(reader.getValue());
reader.moveUp();
package com.thoughtworks.xstream.examples;이 변환기를 등록하보죠. 우리 프로그램의 main() 메소드는 보는 것 같습니다.
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class PersonConverter implements Converter {
public boolean canConvert(Class clazz) {
return clazz.equals(Person.class);
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
Person person = (Person) value;
writer.startNode("fullname");
writer.setValue(person.getName());
writer.endNode();
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Person person = new Person();
reader.moveDown();
person.setName(reader.getValue());
reader.moveUp();
return person;
}
}
package com.thoughtworks.xstream.examples;어떻게 변환기를 등록하는지 보셨나요? 단순히 registerConver()를 호출하면 됩니다.
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class PersonTest {
public static void main(String[] args) {
Person person = new Person();
person.setName("Guilherme");
XStream xStream = new XStream(new DomDriver());
xStream.registerConverter(new PersonConverter());
xStream.alias("person", Person.class);
System.out.println(xStream.toXML(person));
}
}
xStream.registerConverter(new PersonConverter());최종 결과는 이렇습니다.
<person>아마 이렇게 말할겁니다. "tree만 바뀌었잖아! 나는 데이터를 변환하고 싶어!" 라고......
<fullname>Guilherme</fullname>
</person>
package com.thoughtworks.xstream.examples;이 번 경우에는 변환기를 이렇게 단순화 할 수 있습니다.
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return getName();
}
}
package com.thoughtworks.xstream.examples;우리 XML이 더 깔끔해졌을 뿐 아니라 Person 클래스의 별칭(alias) 덕분에 단순히지기까지 했습니다. 문자열 표현이 완성되었으니 이제 내포된 요소(fullname 요소)는 더 이상 필요 없습니다.
import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
public class PersonConverter extends AbstractSingleValueConverter {
public boolean canConvert(Class clazz) {
return clazz.equals(Person.class);
}
public Object fromString(String str) {
Person person = new Person();
person.setName(string);
return person;
}
}
<person>Guilherme</person>날짜 변환기
package com.thoughtworks.xstream.examples;이제 Calendar를 상속한 모든 객체를 변환 하도록 하겠습니다. 이는 어떤 클래스가 Calendar 클래스에 대응이 될 수 있다면 그 클래스는 추상 클래스 Calendar를 상속했다는 뜻입니다.
import java.util.Locale;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class DateConverter implements Converter {
private Locale locale;
public DateConverter(Locale locale) {
super();
this.locale = locale;
}
public boolean canConvert(Class clazz) {
return false;
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
return null;
}
}
public boolean canConvert(Class clazz) {
return Calendar.class.isAssignableFrom(clazz);
}Calendar를 지역화된 문자열로 변화해보겠습니다. 먼저 객체를 Calendar로 캐스팅 하고 Date를 추출한 다음 지역화된 문자열로 변화하는 날짜 변환기를 얻기 위해 DateFormat 팩토리 메소드를 사용하겠습니다.public void marshal(Object value, HierarchicalStreamWriter writer,그리고 이번에는 반대로 복원하기 위해서 GregorianCaldendar를 만들고 지역화된 DateFormat 인스턴스를 얻은 다음 문자열을 파싱해서 Date로 만들고 이 날짜를 원래의 GregorianCaldendar에 넣도록 합니다.
MarshallingContext context) {
Calendar calendar = (Calendar) value;
// grabs the date
Date date = calendar.getTime();
// grabs the formatter
DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
this.locale);
// formats and sets the value
writer.setValue(formatter.format(date));
}
public Object unmarshal(HierarchicalStreamReader reader,Note 1 : 어떤 DateFormat 구현체는 쓰레드에서 안전하지 않다는 것을 기억하십시오. 따라서, 이 포멧터를 변환기의 멤버 변수로 사용해서는 안됩니다.
UnmarshallingContext context) {
// creates the calendar
GregorianCalendar calendar = new GregorianCalendar();
// grabs the converter
DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
this.locale);
// parses the string and sets the time
try {
calendar.setTime(formatter.parse(reader.getValue()));
} catch (ParseException e) {
throw new ConversionException(e.getMessage(), e);
}
// returns the new object
return calendar;
}
package com.thoughtworks.xstream.examples;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class DateConverter implements Converter {
private Locale locale;
public DateConverter(Locale locale) {
super();
this.locale = locale;
}
public boolean canConvert(Class clazz) {
return Calendar.class.isAssignableFrom(clazz);
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
Calendar calendar = (Calendar) value;
Date date = calendar.getTime();
DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
this.locale);
writer.setValue(formatter.format(date));
확인을 해보겠습니다. main 메소드를 갖는 DateTest를 만듭니다.
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
GregorianCalendar calendar = new GregorianCalendar();
DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
this.locale);
try {
calendar.setTime(formatter.parse(reader.getValue()));
} catch (ParseException e) {
throw new ConversionException(e.getMessage(), e);
}
return calendar;
}
}
package com.thoughtworks.xstream.examples;결과는 로케일에 따라 다르겠지만 이런 형식입니다.
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class DateTest {
public static void main(String[] args) {
// grabs the current date from the virtual machine
Calendar calendar = new GregorianCalendar();
// creates the xstream
XStream xStream = new XStream(new DomDriver());
// brazilian portuguese locale
xStream.registerConverter(new DateConverter(new Locale("ko", "KR")));
// prints the result
System.out.println(xStream.toXML(calendar));
}
}
<gregorian-calendar>2006년 2월 10일 금요일</gregorian-calendar>Note : 위에서 별칭을 따로 주지 않았지만 GregorianCalendar의 기본 별칭인 gregorian-calendar으로 표시되었습니다.
// loads the calendar from the string그리고 이것을 시스템 로케일의 단축형 날짜 포멧으로 출력해보죠.
Calendar loaded = (Calendar) xStream
.fromXML("<gregorian-calendar>2006년 2월 10일 금요일</gregorian-calendar>");
// prints using the system defined locale결과는 아마도 이렇게 나올 것입니다. (시스템 로케일이 미국 영어라면)
System.out.println(DateFormat.getDateInstance(DateFormat.SHORT).format(
loaded.getTime()));
2/10/06복잡한 변환기
package com.thoughtworks.xstream.examples;XStream은 이 객체를 아무런 문제없이 변환하는 능력을 가지고 있지만 시연을 위해서 직접 변환기를 만들도록 하겠습니다. 이미 만든 Person과 Calendar 변환기를 재사용하는 것이 좋겠습니다. canConvert 메소드는 명백하게 단순합니다. 파생된 객체는 추가 필드를 갖을 수 있기 때문에 여기에서는 변환하지 않겠습니다. 그러나 우리의 멤버 필드와 널 값을 변환하기 위해 XStream이 이미 등록되어 있는 변환기들은 사용하도록 하겠습니다.
public class Birthday {
private Person person;
private Calendar date;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public Calendar getDate() {
return date;
}
public void setDate(Calendar date) {
this.date = date;
}
}
package com.thoughtworks.xstream.examples;만약 Birthday 구현체의 필드가 확실히 null 값을 갖지 않는다면 변환과 복원 과정에서 null 조건을 제거하고 반복문과 테그 비교문을 생략할 수 있습니다.
import java.util.Calendar;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class BirthdayConverter implements Converter {
public boolean canConvert(Class clazz) {
return Birthday.class == clazz;
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
Birthday birthday = (Birthday)value;
if (value.getPerson() != null) {
writer.startNode("person");
context.convertAnother(value.getPerson());
writer.endNode();
}
if (value.getDate() != null) {
writer.startNode("birth");
context.convertAnother(value.getDate());
writer.endNode();
}
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Birthday birthday = new Birthday();
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("person".equals(reader.getNodeName())) {
Person person = (Person)context.convertAnother(birthday, Person.class);
birthday.setPerson(person);
} else if ("birth".equals(reader.getNodeName())) {
Calendar date = (Calendar)context.convertAnother(birthday, Calendar.class);
birthday.setDate(date);
}
reader.moveUp();
}
return birthday;
}
}
package com.thoughtworks.xstream.examples;
import java.util.Calendar;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class BirthdayConverter implements Converter {
public boolean canConvert(Class clazz) {
return Birthday.class == clazz;
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
Birthday birthday = (Birthday)value;
writer.startNode("person");
context.convertAnother(value.getPerson());
writer.endNode();
writer.startNode("birth");
context.convertAnother(value.getDate());
writer.endNode();
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Birthday birthday = new Birthday();
reader.moveDown();
Person person = (Person)context.convertAnother(birthday, Person.class);
birthday.setPerson(person);
reader.moveUp();
reader.moveDown();
Calendar date = (Calendar)context.convertAnother(birthday, Calendar.class);
birthday.setDate(date);
reader.moveUp();
return birthday;
}
}
# by | 2008/01/25 01:25 | 프로그래밍 이야기 | 트랙백 | 덧글(0)
◀ 이전 페이지다음 페이지 ▶