자바에서는 함수 포인터를 제공하지 않는다. 그러나 객체 참조를 사용해서 비슷한 효과를 얻을 수 있다.
- 호출된 객체의 메소드에서 다른 객체의 메소드를 수행하도록 객체를 정의하는 것이 가능하다. 그런 메소드 하나만 달랑 외부에 제공하는 클래스의 인스턴스는 사실상 메소드 포인터의 역할을 한다.

전략 패턴을 사용할 때는 만약에 모든 인스턴스가 기능이 동일할 때 불필요한 객체 생성 비용을 절감하기 위해 싱글톤이어야 한다.

Posted by 서오석
,

중첩 클래스에는 네가지 종류가 있는데, static 맴버 클래스, static이 아닌 맴버 클래스, 익명 클래스, 지역클래스이다. 이 모두를 내부클래스(inner class)라고 한다.

구문적으로 봐선 static과 static이 아닌 맴버 클래스의 유일한 차이점은, static 맴버 클래스의 선언부에 static 수식어가 있다는 것이다. 간단한 예를 들면 이런 것이다.

 static 맴버 클래스  static이 아닌 맴버 클래스
 

public class A{
    private a;

    static class A_1{
    }
    static class A_2{
    }
}
  

public class A{
    private a; 

    class A_1{
    }
    class A_2{
    }
}

구문적으로는 유사하지만 이 두 종류의 중첩 클래스는 많이 다른다.

static이 아닌 멤버 클래스의 각 인스턴스는 자신을 포함하는 외곽 클래스의 인스턴스와 은연 중에 연관이 된다. 따라서 static이 아닌 멤버 클래스의 인스턴스 메소드 내부에서 외곽 클래스의 인스턴스 메소드를 호출하거나, 또는 this 키워드를 사용해서 외곽 클래스의 인스턴스에 대한 참조를 얻을 수 있다.
private static맴버 클래스의 경우 외곽클래스를 나타내는 객체 컴포넌트 표현에 사용하는데 적합다. 불필요하게 외부에 객체 참조를 제공하지 않아 리소스 낭비가 없어진다.
만일 외곽 클래스의 인스턴스를 사용할 필요가 없는 멤버 클래스를 선언한다면, 항상 static 수신자를 선언부에 추가하여 static 맴버 클래스로 만들자.

익명 클래스는 함수 객체를 생성하는데 많이 사용된다. 또한 Runnalbe, Thread, TimerTask 등의 인스턴스와 같은 프로세스 객체를 생성하는데도 많이 사용한다. 그리고 static 팩토리 메소드 내부에서도 사용된다.

지역클래스는 지역 변수가 선언 될 수 있는 곳이면 어디든 선언될 수 있으며, 지역 변수와 동일한 유효 범위를 갖는다. 또한 다른 종류의 중첩 클래스와 동일한 속성을 갖는다. 맴버 클래스처럼 이름을 가질 수 있어 반복적으로 사용될 수 있으며 static이 아닌 상황일 때에 한해서 익명 클래스 처럼 외곽 인스턴스를 가지며 static 멤버를 포함할 수 없다.

만일 중첩 클래스가 메소드 외부에서 접근할 필요가 있거나 코드가 너무 길어서 메소드 내부에 두기 적합하지 않다면 멤버 클래스를 사용하자. 만일 멤버 클래스의 각 인스턴스가 외곽 클래스의 인스턴스를 참조할 필요가 있다면 static 이 아닌 멤버 클래스로 만들고 그렇지 않다면 static으로 만든다. 클래스가 어떤 메소드 내부에 속한다는 가정하에, 만일 한 곳에서만 그 클래스의 인스턴스를 생성할 필요가 있고 그 클래스의 특성을 나타내는 타입이 이미 존재한다면, 익명 클래스로 만들고 그렇지 않으면 지역클래스로 만든다.
Posted by 서오석
,

우선 용어정의 부터 하자.

 용어  사용 예
 매개화 변수타입(Parameterized type) List<String> 
 실 타입 매개변수(Actual type parameter) String 
 제네릭 타입(Generic type) List<E> 
 형식 타입 매개변수(Formal type parameter)
 언바운드 와일드 카드 타입
(Unbounded wildcard type)
 List<?>
 원천 타입(Raw type)  List
 바운드 타입 매개변수
(Bounded type parameter)
 <E extends Number>
 재귀적 타입 바운드(Recursive type bound) <T extends Comparable<T>> 
 바운드 와일드 카드 타입
(Bounded wildcard type)
List<? extends Number> 
 제네릭 메소드(Generic method) static <E> List<E> asList(E[] a) 
 타입 토큰(Type token) String.class 

하나 이상의 타입 매개 변수를 선언하고 있는 클래스나 인터페이스를 제네릭 클래스, 또는 제네릭 인터페이스라고 부른다. 각 제네릭 타입에서는 매개변수화 타입들을 정의한다.
정의 하는 법은 클래스나 인터페이스 이름 다음에 <> 를 사용해서 매개변수들을 나타내는데. 실 타입 매개변수들은 제네릭 타입의 형식 타입 매개변수와 각각 대응된다. 
각 제네릭 타입에서는 raw 타입을 정의하는데 원천 타입이란 실 타입 매개변수가 없이 사용되는 제네릭 타입 이름을 말한다. (ex - List 이렇게)
이 경우 타입 매개변수를 주지 않고 커렉션 타입이나 다른 제네릭 타입을 사용할 수 있지만 그렇게 해서는 안된다. 왜냐면 원천 타입을 사용하면 제네릭의 장점인 타입 안전과 표현력 모두를 포기하는 것이기 때문이다. 
이걸 막지 않은 이유는 호환성 때문인데 새 코드에서는 되도록 원천 타입을 사용해선 안된다.

간단한 예로  List와 같은 원천 타입을 ㅅ용하면 타입의 안정성을 상실하지만 List<Object>과 같은 매개변수 타입을 사용하면 그렇지 않다.

향후에 작성하는 새 코드에는 원천 타입을 사용하지 않는다는 규칙에도 두가지 예외가 있는데 런타임 시에는 제네릭 타입의 정보가 없어진다는 것 때문에 가능하다.
첫 번째로 원천 타입은 클래스 리터럴의 형태로 사용해야 한다. 자바 명세서에 보면 원천 타입을 매겨변수화 타입과 사용할 수 없다. 즉 String[].class, int.class는 모두 적법하지만 , List<String>.class와 List<?>.class는 허용되지 않는다.
두 번째로 instanceof 연산자와 관련된 것이다. 제네릭 타입의 정보는 런타임 시에 없어지므로 언바운드 와일드 카드 타이이 아닌 다른 매개변수화 타입에 대해 instanceof 연산자를 사용할 수 없다.
Posted by 서오석
,

개발하면서 경고메시지가 나오는데 가능한 모든 uncheck 경고 메시지를 없애자.
만일 모든 경고 메시지를 없앤다면, 코드의 타입 안전이 보장되므로 대단히 좋은 일이다.

- @Suppress-Warnings("unchecked") 주석을 사용해서 경고 메시지를 안 나타나게 억제할 수 있다.
하지만 해당 코드가 타입 안전을 보장하는지 확실치 않은 상태에서 경고 메시지를 억제하면, 우리 스스로 안전 불감증을 갖는 셈이 되어 경고 메시지 없이 코드는 컴파일 되겠지만 런타임 시에는 여전히 ClassCastException 예외를 발생시킬 수 있다. 그리고 SuppressWarnings 주석은 가급적 제일 작은 범위로 사용하자.
절대로 하나의 클래스 전체에 대해 Suppress-Warnings 수적을 사용하지 말자. 그렇게하면 중요하지 않음 메시지들 때문에 정말 중요한 메시지를 알아보기 어렵기 때문이다.

- @Suppress-Warnings("unchecked") 주석을 사용할 때는 그 이유를 주석으로 추가하자 . 그렇게 하면 다른 사람들이 코드를 이해하는데 도움이 될 것이다.

즉.
unchecked 경고 메시지는 중요하므로 무시하지 말자. 모든 unchecked 경고 메시지는 런타임 시에 ClassCastException 예외가 생길 수 있다는 것을 나타낸다.

Posted by 서오석
,

java.lang.Object 클래스는 toString 메소드를 구현하고 있다. 근데 이 메소드에서 반환하는 문자열은 우리가 생각하는 형태가 아니다. 예를 들어 PhoneNumber@163b91 형태로 반환하게 된다. 이러면 보기도 힘들고 그닥 좋지 않다.

toString 메소드를 잘 구현하면 좀더 클래스를 편하게 사용할 수 있다.

근데 이 기능은 예전에 이클립스 3.4에서는 플러그인을 깔아서 했어야 하는데 3.5부터는 이클립스에서 자동으로 오버라이딩 메소드를 만들어주는 기능이 있어서 그냥 그걸 쓰면 된다.
Posted by 서오석
,

equals 메소드를 오버라이드 하는 모든 클래스에서는 반드시 hashcode 메소드도 오버라이드 해야 한다. 그렇지 않으면 Obejct.hashcode 메소드의 보편적 계약을 위반하게 된다.

자바 API 문서의 계약 사항은 다음과 같다.

  • 애플리케이션 실행 중에 같은 객체에 대해 한 번 이상 호출 되더라도 hashCode 메소드는 같은 정수를 일관성 있게 반환해야 한다.
  • equals(Object) 메소드 호출 결과 두 객체가 동일하다면, 두 객체 각각에 대해 hashCode 메소드를 호출 했을 때 같은 정수 값이 나와야 한다.
  • equals(Object) 메소드 호출 결과 두 객체가 다르다고 해서 두 객체 각각에 대해 hashCode 메소드를 호출 했을 때 반드시 다른 정수 값이 나올 필요는 없다. 그러나 같지 않은 객체들에 대해 hashCode 메소드에서 서로 다른 정수 값을 반환하면 이 메소드를 사용하는 해시 컬렉션들의 성능을 향상시킬 수 있음을 알아야 한다.

중요한 것은 hashCode의 오버라이딩에 실패했을 때 두번째 조항 즉 동일한 객체들은 같은 해시 코드 값을 가져야 한다는 것이 위배된다.

 import java.util.*;

public final class PhoneNumber {
    private final short areaCode;
    private final short prefix;
    private final short lineNumber;

    public PhoneNumber(int areaCode, int prefix,
                       int lineNumber) {
        rangeCheck(areaCode,    999, "area code");
        rangeCheck(prefix,      999, "prefix");
        rangeCheck(lineNumber, 9999, "line number");
        this.areaCode  = (short) areaCode;
        this.prefix  = (short) prefix;
        this.lineNumber = (short) lineNumber;
    }

    private static void rangeCheck(int arg, int max,
                                   String name) {
        if (arg < 0 || arg > max)
           throw new IllegalArgumentException(name +": " + arg);
    }

    @Override public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof PhoneNumber))
            return false;
        PhoneNumber pn = (PhoneNumber)o;
        return pn.lineNumber == lineNumber
            && pn.prefix  == prefix
            && pn.areaCode  == areaCode;
    }

위 소스에서는 hashCode에 대한 오버라이딩 메소드가 없기 때문에 문제가 된다.
아래와 같이 클래스를 HashMap과 함께 사용한다고 해보자

     public static void main(String[] args) {
        Map<PhoneNumber, String> m
            = new HashMap<PhoneNumber, String>();
        m.put(new PhoneNumber(707, 867, 5309), "Jenny");
        System.out.println(m.get(new PhoneNumber(707, 867, 5309)));
    }
}

이 시점에서 m.get(new PhoneNumber(707, 867, 5309), "Jenny");을 호출하면 "Jenny"가 반환될 것 같지만 실제로는 null이 반환된다. 여기서 두개의 PhoneNumber 인스턴스가 개입되어 있는데 하나는 HashMap에 삽입할 때 키로 사용되었고 첫번째와 동일한 값을 갖는 두번째 인스턴스는 검색 키에서 사용되었다. 즉 PhoneNumber 클래스에서 hashCode 메소드를 오버라이드 하지 않아서 두 인스턴스가 서로 다른 해시 코드 값을 갖게 된 것이다.
 하지만 모든 객체가 다 똑같은 해시 코드를 갖게 되서는 안된다. 좋은 해시 메소드는 동일하지 않은 객체들에 대해 서로 다른 해시 코드를 만든다. 바로 이것이 hashCode 계약의 세번째 조항의 의미이다.

 @Override public int hashCode() {
      int result = 17;
      result = 31 * result + areaCode;
      result = 31 * result + prefix;
      result = 31 * result + lineNumber;
      return result;
  }

이 메소드에서는 PhoneNumber 인스턴스의 세가지 중요한 필드만으로도 간단히 연산한 결과를 반환하므로 동일한 PhoneNumber 인스턴스들은 똑같은 해시 코드 값을 갖는다. 이런 형태로 구현해야 한다.

Posted by 서오석
,

equals 메소드의 오버라이딩의 경우 생각보다 간단하지 않다. 아래 조건 중 어느 하나라도 만족하면 그냥 상속받은 그대로 사용하는 것이 낫다.

  • 클래스의 각 인스턴스가 본래부터 유일한 경우
    • 인스턴스가 갖는 값보다 활동하는 개체임을 나타내는 것이 더 중요한 Thread와 같은 클래스가 해당되며 이런 것들은 놀리적인 비교가 의미가 없다. 그래서 그냥 Object equals를 사용하면 된다.
  • 두 인스턴스가 논리적으로 같은지 검사하지 않아도 되는 클래스
    • 굳이 두 인스턴스를 비교할 필요 없으면 만들지 말자.
  • 수퍼 클래스에서 equals 메소드를 이미 오버라이딩 했고 그 메소드를 그대로 사용해도 좋은 경우.
    • Set 인터페이스를 구현하는 대부분의 클래스들은 AbstractList로부터, Map의 경우는 AbstractMap에서 상속 받아 사용한다.
  • private이나 패키지 전용 클래스라서 이 클래스의 equals 메소드가 절대 호출되지 않아야 할 경우.
    • 우연히 호출될 수 있는 그런 상황에서는 다음과 같이 equals메소드를 반드시 오버라이딩해서 호출되지 않도록 한다.

열거형(enum) 같은 경우 오버라이드를 할 필요가 없다. 그리고 equals를 오버라이딩 하는 이유는 객체 참조만으로 인스턴스가 동일한지 여부를 판단하는게 아니라, 인스턴스가 갖는 값을 비교해서 논리적으로 같은지 판단할 필요가 있을 때 하는 것이다.

뭐.. 굳이 오버라이딩을 해서 구현하겠다면 아래의 규칙은 지켜야 한다.

  • 재귀적(Reflexive) : null이 아닌 모든 참조 값 x에 대해, x.equals(x)는 반드시 true를 반환해야 한다.
  • 대칭적(Symmetric) : null이 아닌 모든 참조 값 x와 y에 대해, y.equals(x) 가 true를 반환한다면 x.equals(y)도 반드시 true를 반환해야 한다.
  • 이행적(Transitive) : null이 아닌 모든 참조 값 x, y, z에 대해, 만일 x.equals(y)가 true를 반환하고 y.equals(z)가 true를 반환한다면 x.equals(z)도 반드시 true를 반환해야 한다.
  • 일관적(Consistent) : null이 아닌 모든 참조 값 x와 y에 대해, equals 메소드에서 객체 비교 시 사용하는 정보가 변경되지 않는다면, x.equals(y)를 여러 번 호출하더라도 일관성 있게 true 또는 false를 반환해야 한다.
  • null이 아닌 모든 참조 값 x에 대해, x.equals(null)은 반드시 false를 반환해야 한다.

 

 

Posted by 서오석
,

 파이널라이저는 예측 불가에다가 위험하기도 하며 일반적으로는 불필요하다.
파이널라이저의 경우 신속하게 실행된다는 보장이 없다. 즉 객체가 사용할 수 없게 되는 시점부터 파이널라이저가 실행되는 시점까지 긴 시간이 소요될 수 있다.
 즉 파이널라이저 내부에서 실행 시간이 매우 중요한 작업을 절대 하면 안된다. 예를 들어 파일을 닫는 경우이다.
이 경우 파일이 언제 닫힐 지 몰라 어플리케이션에 문제가 생길 수 도 있다.

파이널라이저의 실행 시점이 늦는 것은 이론 상의 문제만은 아니다. 클래스에 파이널라이저를 사용하면 간혹 인스턴스들의 매모리 회수와 재활용이 지연될 수 있다. 즉 사용을 하지 말자 되도록이면 try catch fanally 로 구문으로 확실하게 실행시키도록 하자.

Posted by 서오석
,

자바는 가비지컬랙션이 알아서 메모리를 회수해 주는데 이게 만능이 아니다.
아래 코드를 보자.

 import java.util.*;

public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        return elements[--size];
    }

    /**
     * Ensure space for at least one more element, roughly
     * doubling the capacity each time the array needs to grow.
     */
    private void ensureCapacity() {
        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}


저걸 보면 메모리 누수가 생기는 부분을 찾을 수 있다 어디냐면 pop() 부부인데
elements를 꺼네오고 나서 아무런 행동을 하지 않기 때문에 해당 공간은 메모리에 그대로 남아있게 되고
그러다보면 프로그램이 메모리 부족으로 죽을 수 도 있다.

그래서 저 부분을 이렇게 수정해주어야 한다.

    public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        Object result =  elements[--size];
        elements[size] = null;
        return result;
    }
이렇게 하면 필요없는 참조를 null로 만들게 되어 가비지컬랙션이 해당 부분의 메모리를 수거해 간다.

매모리 누출이 흔히 생기는 이유는 객체 참조를 놔눠서 생기는 부분 말구 또 따른 이유는 캐시이다. 객체 참조를 캐시에 저장하면 저장했다는 것을 잊어버리고 객체가 더 이상 필요 없을 때 까지 캐시에 내벼려 두기 쉽다. 그리고 마지막으로 리스너와 콜백을 사용할 경우 매모리 누수가 발생할 수 있다.
Posted by 서오석
,

불변 클래스의 불필요한 객체 생성을 막으려면 생성자 보다는 static factory method를 사용하는 것이 좋다.

아래 예를 들어보자

 import java.util.*;

public class Person {
    private final Date birthDate;

    public Person(Date birthDate) {
        // Defensive copy - see Item 39
        this.birthDate = new Date(birthDate.getTime());
    }

    // Other fields, methods omitted

    // DON'T DO THIS!
    public boolean isBabyBoomer() {
        // Unnecessary allocation of expensive object
        Calendar gmtCal =
            Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomStart = gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomEnd = gmtCal.getTime();
        return birthDate.compareTo(boomStart) >= 0 &&
               birthDate.compareTo(boomEnd)   <  0;
    }
}


위의 코드를 보면 Calendar 객체와 TimeZone 객체 및 두 개의 Date 인스턴스를 불필요하게 생성한다.

윗 부분을 아래와 같이 고치면 좀 더 성능이 좋아진다.

 import java.util.*;

class Person {
    private final Date birthDate;

    public Person(Date birthDate) {
        // Defensive copy - see Item 39
        this.birthDate = new Date(birthDate.getTime());
    }

    // Other fields, methods

    /**
     * The starting and ending dates of the baby boom.
     */
    private static final Date BOOM_START;
    private static final Date BOOM_END;

    static {
        Calendar gmtCal =
            Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_START = gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_END = gmtCal.getTime();
    }

    public boolean isBabyBoomer() {
        return birthDate.compareTo(BOOM_START) >= 0 &&
               birthDate.compareTo(BOOM_END)   <  0;
    }
}


근데 위에 성능이 좋아지긴 하지만 항상 그런 건 또 아니다.
이유는 Calender 인스턴스의 생성비용이 많이 들기도 하지만 isBabyBoomer 메소드가 아예 호출되지 않는다면 BOOM_START와 BOOM_END가 쓸데없이 초기화되기 때문이다. 근데 이걸 배제하려면 lazy initialization을 해야하는데 그닥 권장하지 않는다.

자바에서는 오토박싱(autoboxing)이란 것으로 불필요한 객체를 생성하게 되는데 이건 기본형 데이터를 이에 대응되는 박스화 기본현 클래스 객체로 자동 변환을 해주는 기능이다. 이와 반대로 하는 것이 오토언박싱(autounboxing)이다.

Long sum = 0L;  <- 오토박싱된다.

문제는 이러면 엄청나게 느려질 수 있다. 그러니까 의도하지 않은 오토박싱이 생기지 않도록 주의해야 한다.

Posted by 서오석
,

유틸리티 클래스 들은 인스턴스를 생성하지 못하게 설계되어 있다. 인스턴스 생성이 의미가 없기 때문인데, 그런 클래스라고 해도 명시적으로 지정한 생성자가 없을 때는 컴파일러가 default 생성자를 만들어준다.
이 경우 클래스 사용자 입장에서는 이 생성자가 다른 것과 차이가 없으며 javadoc 프로그램으로 생성하는 api 문서에도 나타나므로 인스턴스 생성이 가능한 클래스로 오인될 수 있다.
그러므로 생성자 호출을 통한 인스턴스 생성을 방지하고 api문서에도 나타나지 않도록 할 수 있는 방법이 필요하다.

이걸 간단하게 없애주는 거싱 있다. 디폴트 생성자는 명시적으로 지정한 생성자가 전혀 없을 때만 자동으로 만들어진다. 따라서 private 생성자를 정의하면 인스턴스 생성이 불가능한 클래스를 만들 수 있다.

 public class IdiomCode {
 
  //디폴트 생성자가 자동으로 생성되는 것을 방지
  private IdiomCode(){
   throw new AssertionError();
  }
}

이렇게 처리하면 된다.
Posted by 서오석
,

싱글톤(singleton)은 정확히 하나의 인스턴스만 생성되는 클래스이다.
싱글톤을 구현하는 방법은 3가지 방법이 있는데 가장 좋은 방법만 아래 예제와 함께 설명을 하도록 하겠다.

// Enum singleton - the preferred approach - page 18
public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}


싱글톤을 구현하는 방법 중 enum형을 이용해서 하는 방법이 좋다. 하나의 요소를 갖는 enum타입을 만들면 된다.

이 방법은 public 필드 방법과 기능적으로 동일하지만 더 간단하며 복잡한 직렬화 리플렉션 상황에서도 직력화가 자동으로 지원되고 인스턴스가 여러개 생기지 않도록 확실하게 보장해준다.
Posted by 서오석
,

여러 필드의 값을 받는 클래스의 경우 텔리스코핑 생성자 패턴을 사용하면 매번 인스턴스를 생성할 때 원하지 않는 매개변수에도 초기값을 주어야 한다.
매개변수가 많은 생성자의 경우 두번째 대안으로 자바빈즈 패턴(getter, setter를 사용하는 것)이 있는데 이것은 여러 번의 메소드 호출로 나누어져 인스턴스로 생성되므로, 생성 과정을 거치는 동안 자바빈 객체가 일관된 상태를 유지하지 못할 수 있다.
 또한 자바빈즈 패턴은 불변 클래스를 만들 수 있는 가능성을 배제함으로 thread에서 안정성을 유지하려면 개발을 할 때 추가적인 노력이 필요하다는 단점이 있다.

이래서 나온게 빌더 패턴이다.

예제는 다음과 같다

 
public class NutFacts {
 
 private final int servingSize;
 private final int calories;
 private final int fat;
 private final int sodium;
 
 public static class Builder {
  
  private final int servingSize; //필수 값
  
  private int calories; //선택값
  private int fat;  //선택값
  private int sodium; //선택값
  
  public Builder(int servingSize){
   this.servingSize = servingSize;
  }
  
  public Builder calories(int val){
   calories = val;
   return this;
  }
  
  public Builder fat(int val){
   fat = val;
   return this;
  }
  
  public Builder sodium(int val){
   sodium = val;
   return this;
  }
  
  public NutFacts build(){
   return new NutFacts(this);
  }
 }
 
 private NutFacts(Builder builder){
  servingSize = builder.servingSize;
  calories = builder.calories;
  fat = builder.fat;
  sodium = builder.sodium;
 }

}

 public class item2 {

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub

  NutFacts drink = new NutFacts.Builder(3).calories(23).build();
 }

}


그냥 자바 빈즈 패턴의 경우 getter와 setter를 만들어서 썼기 때문에 코드가 길어진다. 하지만 아래 class item2 클래스를 보면 자신이 집어넣을 필수 파라미터(Builder)를 설정하고 뒤에 옵셔널한 파라미터(calories)를 설정하도록 한다.

생성자가 static factory method에서 많은 매개 변수를 갖게 될 클래스를 설계할 때는 빌더 패턴이 좋은 선택이다. 특히 선택 매개변수가 대부분인 경우 그렇다. 그리고 그 간 사용하던 텔리스코핑 생성자 패턴보다 빌더를 사용하면 코드의 가독성이 좋고 작성도 쉬워진다. 또한 빌더는 자바빈즈 패턴보다 훨씬 안전하다.

위의 예제보다 더 좋은 예가 있는 곳을 찾았다.
아래 링크를 따라가면 마린의 속성 정보를 가지고 빌더패턴을 적용한 소스가 있다.
http://dbjavayo.tistory.com/entry/h2-생성자의-매ㅑ개변수가-많을-때는-빌더builder를-고려하자


Posted by 서오석
,

클래스 사용자가 크 클래스의 인스턴스를 생성하는 방법 중 유용하게 사용할 수 있는 방법은 클래스에 public static factory 메소드를 두는 것이다. 이것은 static 메소드로써 그 클래스의 인스턴스 하나를 생성하여 반환한다.

즉. 아래와 같은 형태로 개발을 하는 것이다.
 public static Bollean valueOf(boolean b){
return b ? Boolean.TRIE : Boolean.FALSE;
}

장점
생성자 대신 static factory method를 제공할때의 장점
1. 자기 나름의 이름을 가질수 있다.
자바 클래스는 동일한 시그니처를 갖는 생성자를 하나만 가질 수 있게 되어 있다. 그래서 개발을 할 때 파라미터의 순서만 바꿔서 두 개의 생성자를 만드는 경우가 있는데 이런 때는 나중에 실수를 할 가능성이 크다. static factory method는 자신의 이름을 가질 수 있기 때문에 생성자와 같은 제약을 받지 않는다. 하나으 ㅣ클래스에 동일한 시그니처를 갖는 여러개의 생성자가 필요한 경우 생성자 대신 static factory method를 사용하자.

2. 호출할때마다 매번 새로운 객체를 생성할 필요가 없어진다.
불변 클래스의 경우 이미 생성된 인스턴스를 다시 사용할 수 있으며, 불필요하게 중복된 인스턴스들이 생성되는 것을 방지하기 위해 이미 생성된 인스턴스들을 저장했다가 반복 사용할 수 있다. static factory method는 여러번 호출되더라도 이미 생성된 동일 객체를 반환할 수 있으므로 클래스에서는 언제든지 인스턴스들의 존재를 직접 제어할 수 있다.
3. 자신의 클래스 인스턴스만 반환하는 생성자와 달리, 자신이 반환하는 타입의 어떤 서브타입 객체도 반환할 수 있다.

4. 매개변수화 타입의 인스턴스를 생성하는 코드를 간결하게 해준다.
parameterized 클래스의 생성자를 호출할 때는 type parameter를 지정하는데 그럼 이렇게 코딩해야 한다.
 Map<String, List<String>> map = new HashMap<String, List<String>>();
이렇게 하면 타이핑할 분량도 많아지고 소스도 복잡해진다. 그래서 이것을 static factory를 사용해서 아래와 같이 바꿔 사용한다.
     public static <K,V> HashMap<K, V> newInstance(){
     return new HashMap<K, V>();
    }

사용시
        Map<String, List<String>> m = UsingPoi.newInstance();
* UsingPoi는 클래스 이름.

단점
1.인스턴스 생성을 위한 static factory 메소드만 가지고 있으면서, public이나 protected 생성자가 없는 클래스의 경우는 서브 클래스를 가질수 없다

2. 다른 static 메소드와 햇갈릴수 있다.
이 부분은 클래스나 인터페이스 주석으로 표기를 하거나 공통적인 작명 규칙을 만들고 지킴으로써 단점을 줄일 수 있다.
  • valueOf - 자신의 매개변ㅅ와 같은 값을 갖는 인스턴스를 반환한다.
  • of - valueOf를 줄인 형태의 이름이면 EnumSet에서 사용한다.
  • getInstace - 매개변수에 나타난 인스턴스를 반환하지만 매개변수와 같은 값을 갖지 않는 경우도 있다. (싱글톤)
  • newInstance - getInstance와 유사하나 반환되는 각 인스턴스가 서로 다르다.
  • getType - getInstance와 유사하나 factory method가 다른 클래스에 있을 때 사용한다.

Posted by 서오석
,