Whiteship's Note


Effective Java Reloaded

Java : 2008.10.26 09:49


참조: Parleys.com에서 Effective Java Reloaded

Generic

- PECS, Producer extends, Consumer super.
- 타입제한으로 인해 다형성같은 유연함이 떨어질 땐, PECS를 적용하면 유연하게 만들 수 있다.

Enum

- int 값을 얻고 싶을 때 ordinal()+1 을 반환하는건 유지보수 힘들다.(순서 바뀔 수도 있고, 중간 요소가 빠져있을 수도 있고, 같은 수를 반환할 수가 없다.)
- int 속성을 enum 생성자에 전달하고 그 값을 반환하도록 코딩한다.(오.. 멋지다.)
public enum Ensemble {
  SOLO(1), DUET(2), TRIO(3), ...
 
  private final int numberOfMusicians;

  Ensemble(int size){
    numberOfMusicians = size;
  }
 
  public int numberOfMusicians() {
    return numberOfMusicians;
  }

}

- int 상수 사용 문제. not typesafe, no namespace, brittle, 출력시 값이 명시적이지가 않아, 순회하기도 힘들어, 64비트 넘으면 ㄷㄷㄷ
- EnumSet을 enum이 가지고 있는 메소드에 넘긴다.
public void applyStyles(Set<Style> styles) {...}

text.applyStyles(EnumSEt.of(Style.BOLD, Style.ITALIC));

- ordinal()은 EnumSet, EnumMap 같은 곳에서 내부적으로 사용할 목적이니까, 프로그래밍 할 땐 쓰지 말도록.

- 싱글톤 퀴즈, enum으로 싱글톤 만들기
 Serializable Singleton

public enum Elvis {
  INSTANCE;
  ...
}

- enum 제약 사항
확장 못 한다. 메소드는 인터페이스를 사용해서 확장 가능
Emulated Extensible Enum

Lazy initialization

- 언제 사용하는가
성능, initialization circularity 해결하려고

- static 필드는 Holder Class 사용하기
private static class FiledHolder {
  static final FieldType field = computeFirldValue();
}

static FieldType getField(){
  return FieldHolder.field;
}

- 인스턴스 필드는 더블 체킹

private volatile FieldType field;

FieldType getField(){
  FieldType result = field;
  if(result == null){  // 1st check (no lock)
    synchronized(this){
      result = field;
      if(result == null)  // 2nd check (w/ lock)
        field = result = computeFieldValue();
    }
  }
  return result;
}





top

Java : 2008.10.26 09:49 Trackback. : Comment.

Singletons and lazy loading

Design Pattern : 2007.01.27 10:40


참조 : http://www.oreillynet.com/onjava/blog/2007/01/singletons_and_lazy_loading.html?CMP=OTC-FP2116136014&ATT=Singletons+and+lazy+loading

싱글톤 패턴이 클래스 달랑 하나여서 제일 간단해 보이는 디자인 패턴임에도 불구하고 Lazy Loading 또는 초기화 지연 기법을 사용하려고 하면 굉장히 복잡해 지네요. 그리고 동기화 문제도 생각하면 syncronized가 필수가 됩니다. 그래서 보통은 아래 처럼 구현을 하죠.

class Singleton {

    private static Singleton instance;

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        return (instance != null) ? instance : (instance = new Singleton())
    }
}

하니면 초기화 지연 기법을 포기하고 아예 처음 변수를 선언할 때 객체도 만들어 버립니다.

class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        return instance;
    }
}

아니면 Head First Design Pattern에도 살짝 나왔던 것 같은데 2중 체크 라는 것을 합니다. 하지만 이것도 되는 것 처럼 보일 뿐 제대로 동작하지 않습니다.

// Correct Double-Checked Locking for 32-bit primitives
class Foo {
    private int cachedHashCode = 0;
    public int hashCode() {
        int h = cachedHashCode;
        if (h == 0)
        synchronized(this) {
            if (cachedHashCode != 0) return cachedHashCode;
            h = computeHashCode();
            cachedHashCode = h;
        }
        return h;
    }
    // other functions and members...
}

예제 코드가 좀 거시기 하지만 얼추 이와 비슷한 코드들이 동기화 관련된 부분에서 자주 보입니다. 이게 제대로 동작하지 않는 이유는 빨간 색 코드를 호출해서 h가 참조할 객체가 만들어 지는 도중에 녹색 조건 문에 다른 쓰레드가 걸려서 다 만들어 지지도 않은 객체의 레퍼런스를 가져갈 수 있기 때문인 것 같은데 이 것에 대한 자세한 내용은 여기를 참조하세요.

Anyway... 그럼 어떻게 하면 좀더 간단하고 빠르게(syncronized를 쓰지 않고) 싱글톤과 초기화 지연 기법을 사용할 수 있을까 하는게 참조한 글의 주제 였는데요.

public class Singleton {

    static class SingletonHolder {
        static Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }

}

이 코드는 위키피디아에 Initialization on Demand Holder를 참조 했다고 합니다. 위 코드를 보시면 먼저 Syncronized가 사라진걸 볼 수 있구요. 따라서 멀티 쓰레드 환경에서 매우 빠르게 동작할 것 같습니다. 그리고 초기화 지연은 어떻게 보장을 할까요. getInstance() 메소드가 호출되면 그 때 static inner 클래스인 SingletonHolder가 JVM에 로딩 되게 되고 그 때서야 객체는 만들어 지게 되는거죠. 그리고 그 이후에는 static이니까 싱글톤.. 단일 객체를 보장하게 되는 겁니다.

'Design Pattern' 카테고리의 다른 글

데코레이터(Decorator) 패턴  (0) 2008.10.01
프록시(Proxy) 패턴  (0) 2008.09.26
JUnit 공부하자.  (0) 2008.09.01
H.F.OOAD 5장 문제  (0) 2007.11.20
상위 클래스 보다는 인터페이스를...  (4) 2007.08.31
Singletons and lazy loading  (2) 2007.01.27
객체지향 디자인 원칙  (2) 2006.12.11
Losely Coupled를 활용하라.  (0) 2006.12.11
상속보다는 구성을 활용한다.  (0) 2006.12.11
바뀌는 부분을 캡슐화 한다.  (0) 2006.12.11
State Pattern 예제  (2) 2006.12.10
top


Lazy Initialization 언제 사용 해야 될까요?

Java : 2006.10.28 14:31


마틴 파울러의 Lazy Initialization을 번역한 글을 보며 간단히 요약해 봅니다.

  • Lazy Initialization이란?
  • 언제 사용하는 것이 좋은가?
  • 언제 사용하지 않는 것이 좋은가?

  • Lazy Initialization이란?
먼저 Lazy Initialization이란 어려운 개념이 아닙니다. 객체를 필요할 때 만들겠다는 것입니다.
Class Student {
    private List<Grade> grades = new ArrayList<Grade>();
...
...
}

이런 경우에는 이 자바 파일을 컴파일 한 class 파일이 JVM에 로딩 될 때 분홍색 문장은
1. grades 라는 레퍼런스 변수가 먼저 만들어지고(저 문장은 원래가 복문이지요.)
2. new ArrayList<Grade>(); 에서 ArrayList 객체가 만들어 집니다.(메모리에 위치하게 되는거죠.)
3. 그리고 그 메모리 위치에 접근할 수 있는 무언가(참조 값(?))를 1번에서 만든 레퍼런스 변수 안에 집어 넣습니다.

즉.. 여기서 2번에서 객체를 만들게 되는것입니다

하지만 Lazy intialization은

Class Student {
  private List<Grade> grades;
  ...
  ...
  public List<Grade> createGrades(){
      if( grades != null)
          return grades;
      return new ArrayList<Grade>();
  }
  ....
}

코드가 급조한거라 적절하지 않을 수 있지만 이 경우에는 class가 JVM에 로딩될 때 객체를 생성하지 않습니다. 그냥 List 타입의 grades 변수만 만들어 지겠죠. 그리고 실제 코드에서 만약에 이 변수를 사용하려면 사용하기 전에 createGrades() 메소드를 먼저 실행해야 합니다. 안그러면 nullPointException이 발생하겠죠. 즉 객체가 필요할 때 만들어 쓰는 방식 입니다. (위의 상황이라면 얼추 싱글톤과 비슷하지만 동기화 문제를 처리하지 않았기 때문에 완벽한 싱글톤이라고 할 순 없겠습니다.)

Lazy initialization is useful when calculating the value of the field is time consuming and you don't want to do it until you actually need the value.

초기화의 지연은 필드 값을 사용하기 전까지는 계산하는 것을 원치 않고, 실제로 값을 사용하는 시점에서 계산하고자 할 때 유용하다.


  • 언제 사용하는 것이 좋은가?
언제 이런 방법을 사용하는 것이 유용 할까요?

it's often useful in situations where the field isn't needed in many contexts or we want to get the object initialized quickly and prefer any delay to be later on.

정의된 필드가 많은 경우 사용되지 않는 상황이나, 초기화를 빨리 수행하고 부가 작업을 뒤로 미루고 싶을 때  적합하다.

정의된 필드가 자주 사용되지 않는 경우.. 즉 드물게 사용되는 경우에 사용하는 것이 좋다는 것이군요. 왜 그럴까요. 자주 쓰이지도 않는데 메모리를 잡아먹고 있어서 그럴까요? 자주 쓰이지 않기 때문에 메모리를 계속 가지고 있게 할 필요가 없다. 그래서 필요할 때만 객체를 생성하게 한다. 이러한 맥락에서는 이해가 가지만..

과연 그 객체를 "필요할 때 마다 생성하는데 드는그 비용과 메모리에서 계속 유지 하고 있을 그 비용의 차이" 이 것도 고려해야 하는 것은 아닌지.. 생각해 봅니다.

  • 언제 사용하지 않는 것이 좋은가?
그럼 언제 이런 방법을 사용하지 않는 것이 좋을까요?

As with any optimization you shouldn't use this technique unless you have a real performance problem to solve.

In particular lazy initialization can cause debugging woes because if you try look at the field during debugging you'll cause a state change in the system that doesn't happen under normal use.

최적화를 통해 성능 저하를 해결해야 하는 상황이 아니라면 초기화의 지연을 사용하지 않아야 한다.


어떤 경우에는 초기화의 지연 기법이 디버깅을 어렵게 할 수 있다.

위의 언제 사용하는 것이 좋은가? 에 나온 경우가 아니라면 사용하지 않아야 한다고 합니다. 이 밖에도 싱글톤 패턴의 경우에 초기화 지연 기법을 사용할 경우 하나의 객체가 아닌 객체 여러개가 생길 수도 있는 문제가 발생하기 도 합니다.
(물론 이 문제의 해결 책이 꼭 빠른 초기화(?)만 있는 것은 아니지만 제일 간단해 보이는 해결책이였습니다.)

ps : 노란색 단락은 마틴 파울러 글의 원문이였고 녹색 단락은 원문을 번역한 글이였습니다.


'Java' 카테고리의 다른 글

i++와 i=i+1 속도 비교  (0) 2006.11.02
Comparator를 사용하여 비교하기.  (0) 2006.11.02
Arrays.sort() & Collections.sort()  (2) 2006.11.02
What is Object?  (12) 2006.11.01
Agile Java 2장 연습문제 풀기  (2) 2006.10.29
Lazy Initialization 언제 사용 해야 될까요?  (9) 2006.10.28
Agile Java 1장 연습문제 풀기  (2) 2006.10.27
JUnit 3.8 과 JUnit 4의 차이  (0) 2006.10.27
Reference의 위험성  (10) 2006.10.27
다중 구현(?)  (2) 2006.10.23
Interface  (2) 2006.10.16
top







티스토리 툴바