7분 완성. CRUD 개발 쑈!!!(with OSAF)

검색하다가 NG가 났는데, 영문 검색은 잘 됩니다. 한글 검색시 인코딩 때문에 문제가 좀 있네요. 전에도 발생했던 문제인데, 톰캣 설정(사부님왈. bodyfor어쩌구..)을 조금 수정하면 간단하게 고칠 수 있을 것 같습니다.

실제 동영상 런타임이 7분 49초인데, 설명 없이 코딩만 샥샥 하면 분명 3분 미만으로 CRUD 개발을 완성할 수 있습니다. 그렇게 만들어 놓고 세부적인 검색 조건과 파라메터 들을 추가하면서 개발 해 나간다면… 캬오… 멋지지 않아요??!!!

2008/10/24 – [Screen Casting] – OSAF 빌드하기
2008/10/28 – [OSAF] – OSAF 샘플 코드 실행하기(스크린캐스팅)

이것으로 OSAF 스크린캐스팅 시리즈 세 개가 완성됐습니다. 캬~
다음 스크린캐스팅은 OSAF 아키타입으로 웹 개발 시작하기. 입니다. 많이 기대해주세요.

ps: 요즘 다운로드 수가 바닦을 치던데.. 받으실 분들은 다 받으신 건가요. 🙂

프로토타입 패턴(Prototype Pattern)

참조: Java 언어로 배우는 디자인 패턴 입문

“복사해서 인스턴스 만들기”
“클래스에서 인스턴스를 만들지 않고 인스턴스에서 인스턴스 만들기”

객체를 클래스를 사용하여 new로 생성하지 않고 복사해서 만들고 싶은 경우
– 취급하는 객체가 다양해서 각각을 별도의 클래스로 두기엔 무리가 있을 때..
– 클래스에서 객체 생성이 어려운 경우(ex. 런타임에 마우스 조작으로 만들어 내는 객체)
– 프레임워크와 생성할 인스턴스를 분리하고 싶을 때. 모형이 되는 객체를 등록해 놓고 그 객체를 복사해서 인스턴스 생성.

예제 코드 from Wikipedia

/** Prototype Class **/
public class Cookie implements Cloneable {

public Object clone() {
try {
Cookie copy = (Cookie)super.clone();

//In an actual implementation of this pattern you might now change references to
//the expensive to produce parts from the copies that are held inside the prototype.

return copy;

}
catch(CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}

/** Concrete Prototypes to clone **/
public class CoconutCookie extends Cookie { }

/** Client Class**/
public class CookieMachine {

private Cookie cookie;//could have been a private Cloneable cookie;

public CookieMachine(Cookie cookie) {
this.cookie = cookie;
}
public Cookie makeCookie() {
return (Cookie)cookie.clone();
}
public static void main(String args[]) {
Cookie tempCookie = null;
Cookie prot = new CoconutCookie();
CookieMachine cm = new CookieMachine(prot);
for (int i=0; i<100; i++)
tempCookie = cm.makeCookie();
}
}

Prototype
– 원형 인터페이스로 객체 복사에 사용할 메소드를 정의한다.
– Cloneable 인터페이스 상속.

ConcretePrototype
– Prototype 인터페이스 구현하여 실제 복사 로직 구현.

Client
– Prototype 인터페이스를 사용하여 새로운 객체를 만든다.

주의할 것.
– Cloenable은 마커 인터페이스.
– 실제 clone 메소드는 Object 클래스에 들어있다.
– 필드 대 필드 복사라서 배열의 경우 레퍼런스를 복사해서 위험할 수 있다. 그럴 땐 직접 clone 메소드를 재정의 해야 한다.
– clone을 재정의 할 떄는 super.clone()을 호출해야 한다.
– clone은 복사만 하지 생성자를 호출하지 않는다. 그래서 생성시에 로직이 필요한 땐 그 로직도 clone에 같이 넣어 준다.
– 자세한건 API를 보시라…

EJ2E Item 11. 적절하게 clone을 재정의하라

참조: Effective Java 2nd Edition Item 11. Override clone judiciously

Cloneable 인터페이스는 mixin 인터페이스(Item 18)로 복제가 가능한 객체임을 나타낼 의도로 만들었다. 하지만, 그런 목적을 제공하는데 실패했다.

이번 항목에서는 어떻게 하면 잘 동작하는 clone 메소드를 구현할지에 대한 것이다.

메소드도 없는 Cloneable 인터페이스는 무엇을 해주는 건가? Object의 clone 구현체 행위를 결정한다. 만약 해당 클래스가 Cloneable을 구현했다면, Obejct의 clone 메소드는 객체의 모든 필드를 복사한 것을 반환한다. Cloneable 인터페이스를 구현하지 않았으면, CloneNotSupportedException을 던진다.

JavaSE6 java.lang.Object 표준에서 정의한 clone 메소드 general contract

x.clone() != x // true
x.clone().getClass() == x.getClass() // true
x.clone().equals(x) // true
생성자는 호출하지 않는다.

문제
– 생성자를 호출하지 않는다는 조항은 너무 강하다.
– x.clone().getClass() == x.getClass() 조항은 너무 약하다.

…중간 어렵다..-_-;;

음.. 결론은.. final이 아닌 클래스의 clone 메소드를 재정의할 땐, super.clone 호출해서 얻어온 객체를 반환해야 한다.

@Override public PhoneNumber clone() {
  try {
    return (PhoneNumber) super.clone();
  } catch(CloneNotSupportedException e) {
    throw new AssertionError();  // Can’t happen
  }
}

Object가 아니라 PhoneNumber를 반환하고 있는데, 1.5 부터는 이런 코드도 괜찮다. 1.5에 covariant return type을 도입했기 떄문에 재정의하는 메소드의 반환 타입으로 원래 타입의 하위 타입을 반환할 수도 있다.

mutable 객체를 참조하는 필드(ex. private Object[] elements)를 가지고 있는 객체에서 위와 같은 clone 메소드를 사용하면 문제가 생길 수 있다. super.clone()으로 복사하면, 같은 객체를 참조하게 되고, 그럼 원복과 복사체가 동이한 객체에 대한 레퍼런스를 쥐고 있는거라 위험하다. 따라서 원본 객체와는 별개로 복사해줘야 한다.

@Override public Stack clone() {
  try {
    Stack result = (Stack) super.clone();
    result.elements = elements.clone();
    return result;
  } catch (CloneNotSupportedException e) {
    throw new AssertionError();
  }
}

clone을 재쉬적으로 호출하는 걸로도 안 될 때(ex. private Entry[] buckets)가 있다. 그럴 땐 deep copy.

코드 생략.

아.. 복잡해. 이런게 정말 필요한거야? 만약에 Cloneable 인터페이스를 구현한 클래스를 상속할 땐, 위에 있는걸 전부 신경써서 잘 동작하는 clone 메소드를 구현해야 한다.

그런 경우가 아니라면, 객체 복사 대안책을 사용하던가 아예 이런 기능을 제공하지 않아도 된다. immutable 클래스에서 객체 복사를 제공하는건 말이 안 된다. 복사체를 원본과 구분할 수가 없기 때문에.

첫번째 대안 copy constructor 또는 copy factory를 제공하는 방법이 있다.

복사 생성자(ex, public Yum(Yum yum);)
복사 팩터리(ex. public static Yum newInstance(Yum yum);)

이 방법이 clone보다 더 좋은 이유
– they don’t rely on a risk-prone extralinguistic object creation mechanism
– they don’t demand unenforceable adherence to thinly documented conventions
– they don’t conflict with the proper use of final fields
– they don’t throw unnecessary checked exceptions
– they don’t require casts

기선: clone 안 쓰는데 이거 왜케 어렵나요?
사부님 왈: 프로토타입 패턴에서 쓰는 거다.

EJ2E Item 10. toString은 항상 재정의하라.

참조:  Effective Java 2nd Edition Item 10. Always override toString

JavaSE6 스펙에서 정의한 toString 제약 사항.

“a concise but informative representation that is easy for a person to read”

toString은 println이나 printf를 호출할 때 자동으로 호출된다.

toString을 구현할 때 결정 해야 하는 중요한 것 하나는 문서에서 반환 값 형식에 대해 서술할지 여부다. value class에서는 이렇게 형식을 기술해 주는게 좋은데, 모호하지 않으며, 가독성이 좋기 때문이다. 자바의 BigInteger, BigDecimal 같은 API가 이런 방법을 취하고 있다.

형식을 기술하던 말던, 어쨋거나 문서에 여러분의 의도를 분명하게 해야한다.