donaricano-btn

생성자의 builder패턴 적용(builder pattern)


1. 생성자의 문제

1) 선택적 인자가 많은 상황에 잘 적응하지 못한다


2. 점층적 생성자 패턴(telescroping constructor pattern)

- 필요한 생성자를 하나씩 추가하는 방식

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class NutritionFacts {
     
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
     
    public NutritionFacts(int servingSize, int servings) {
        this(servingSize, servings, 0, 0);
    }
 
    public NutritionFacts(int servingSize, int servings, int calories) {
        this(servingSize, servings, calories, 0);
    }
     
    public NutritionFacts(int servingSize, int servings, int calories, int fat) {
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = calories;
        this.fat = fat;
    }
     
}

1) 문제점

- 설정할 필요없는 값(위의 0 같은)을 설정해야한다. 

- 점층적 생성자 패턴은 잘 동작하지만 클라이언트 코드 작성이 어렵고 가독성이 떨어진다.


3. 자바빈 패턴(JavaBeans pattern)

- 인자 없는 생성자를 호출하여 객체를 만든다음 설정 메소드로 필드값을 추가하는 방식

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class NutritionFacts {
     
    private int servingSize = -1;
    private int servings = -1;
    private int calories = 0;
    private int fat = 0;
     
     
    public int getServingSize() {
        return servingSize;
    }
    public void setServingSize(int servingSize) {
        this.servingSize = servingSize;
    }
    public int getServings() {
        return servings;
    }
    public void setServings(int servings) {
        this.servings = servings;
    }
    public int getCalories() {
        return calories;
    }
    public void setCalories(int calories) {
        this.calories = calories;
    }
    public int getFat() {
        return fat;
    }
    public void setFat(int fat) {
        this.fat = fat;
    }
     
}

- 사용

1
2
3
4
5
NutritionFacts coke = new NutritionFacts();
 
cock.setServingSize(240);
cock.setServings(100);
....

1) 문제점

- 1회의 함수 호출로 객체 생성을 끝낼수 없다.(객체의 일관성이 일시적으로 깨진다)

- 변경 불가능 클래스(immutable)를 만들 수 없다.


4. 빌더패턴(builder pattern)

- 점층적 생성자 패턴 + 자바빈 패턴

- 필요한 객체를 직접 생성하는 대신, 클라이언트는 먼저 필수 인자들을 생성자에 전부 전달하여 빌더 객체를 만든다.

- 빌더 객체에 정의된 설정 메소드를 호출하어 선택적 인자를 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class NutritionFacts {
     
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
     
    //빌더 패턴 정의
    public static class Builder{
         
        private int servingSize;
        private int servings;
        private int calories;
        private int fat;
         
        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings = servings;
        }
         
        //설정 메소드
        public Builder calories(int val) {
            calories = val;
            return this;
        }
         
        public Builder fat(int val) {
            fat = val;
            return this;
        }
         
        //최종값 반
        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }
     
    private NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
    }
     
}

- 사용

1
NutritionFacts coke = new NutritionFacts.Builder(240, 8).calories(100).fat(0).build();

1) 장점

- 불변식(invariant) 적용이 가능하다. build() 해당 불변식이 위반되었는지 검사가 가능하다.

- 여러개의 varargs 인자를 받을 수 있다.

- 유연하다. 하나의 빌더 객체로 여러 객체를 만들 수 있다.

2) 단점

- 객체를 생성하려면 먼저 빌더 객체를 생성해야한다.



블로그 이미지

리딩리드

,
donaricano-btn

생성자 vs 정적팩토리메소드(Constructor vs static factory method)


- 기존에 객체를 만드는 방법은 public 생성자를 이용하는 방법이다. 그러나 public 으로 선언된 정적 팩토리 매소드를 이용한 객체 생성방법으로 여러 장점을 얻을 수 있다.


1. 정적 팩토리 메소드 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Test {
     
    int max;
     
    //Origin
    public Test(int max){
        this.max = max;
    }
     
    //static factory method
    public static Test getInstance(int max){
        return new Test(max);
    }
     
    public int getInt(){
        return max;
    }
     
     
    public static void main(String[]args){
         
        //Origin
        Test instance = new Test(10);
        System.out.println(instance.getInt());
         
        //static Factory method
        System.out.println(Test.getInstance(20).getInt());
    }
}


2. 정적 팩토리 메소드의 장점

1) 생성자와 달리 이름이 있다.

- 생정자와 달리 사용하기 쉽고 가독성을 높일 수 있다.

- 여러 생성자를 만들었을 때 나타나는 문제를 해결 할 수 있다.

2) 호출 할 때 마다 새로운 객체를 생성할 필요가 없다

- 불필요하게 같은 객체가 생성되는 것을 막을 수 있다.

- 개체 통제 클래스(instance-controlled class) 화 할 수 있다.

* 개체 통제 클래스

- 어떤 시점에 어떤 객체가 얼마나 존재할 지 통제 할 수 있다

3) 생성자와 달리 반환 값 자료형의 하위 자료형 객체를 반환 할 수 있다.

- 반환 되는 객체 클래스를 유연하게 결정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Test {
     
    //static factory method
    public static Test getInstance(int max){
        return new Test();
    }
     
    public static Test getTestType(String name){
        if(name.equals("English")){
            return new EnglishTest();
        }else{
            return new MathTest();
        }
    }  
         
}
 
class EnglishTest extends Test{
     
}
 
class MathTest extends Test{
     
}

4) 형인자 자료형(parameterized type) 객체를 만들 때 편하다

1
2
3
4
5
//origin
Map<String, List<String>> m = new HashMap<String, List<String>>();
 
//static factory method
Map<String, List<String>> m = HashMap.newInstance();

- 기존에는 형인자가 늘어 짐에 따라 길고 복잡한 코드가 만들어진다.

- 정적팩토리 메소드를 사용하면 컴파일러가 형인자를 스스로 알아낸다(자료형 유추( type inference))


3. 정적 팩토리 메소드의 단점

1) 정적 팩토리 메소드만 있는 클래스는 하위 클래스를 만들 수 없다

2) 다른 정적 메소드와 확연히 구분되지 않는다


블로그 이미지

리딩리드

,
donaricano-btn

effectiveJava - for문보다는 for-each문 사용


1. while 문의 헛점

- 지역변수의 유효범위로 인하여 copy-and-paste 버그의 확률이 높다

1
2
3
4
5
6
7
8
9
Iterator<Element> i = c.iterator();
while(i.hasNext()){
  doSomething(i.next());
}
 
Iterator<Element> i2 = c2.iterator();
while(i.hasNext()){ //bug
  doSomething(i2.next());
}

- 처음에 설정된 변수 i 를 다음 while 문에도 사용함


2. for, for-each 형태

1) 1.5 이전 버전

1
2
3
for(Iterator i = c.iterator(); i.hasNext();){
  doSomething((Element) i.next())
}

2) 배열순회

1
2
3
for(int i = 0; i <a.length; i++){
  doSomething(a[i]);
}

3) 1.5 이후 권장(컬렉션, 배열 순회)

1
2
3
for(Element e : elements){
  doSomething(e);
}


3. 왜 for-each 인가

- for 문과 성능은 비슷하지만 상황에 따라 나은 성능을 보이기도 한다.

- 중첩 순환문을 만들 시 좋다.(버그 확률을 낮춘다)

1
2
3
4
5
6
Collection<Face> faces = Arrays.asList(Face.values());
        for(Iterator<Face> i = faces.iterator(); i.hasNext();){
            for(Iterator<Face> j = faces.iterator(); j.hasNext();){
                System.out.println(i.next() + " " + j.next());
            }
        }

- 이중순회 도중 예상된 결과를 반환하지 않는다

1
2
3
4
5
for(Face face1 : faces){
            for(Face face2 : faces){
                System.out.println(face1 + " " + face2);
            }
        }

- for-each를 사용하여 간결하게 표현


4. for-each 문이 무조건 답은 아니다

1) 필터링

- 특정원소를 삭제한다면 iterator의 remove() 호출해서 삭제해야 하기 때문에 제한된다.

2) 변환

- 원소의 값을 수정한다면 제한된다.

3) 병렬순회

- 병렬순회하거나 첨자를 발맞춰 나간다면 제한된다.


블로그 이미지

리딩리드

,
donaricano-btn

effectiveJava - 지역변수의 유효범위 최소화


1. Tip

- 지역변수를 처음 사용하는 곳에 선언하라

- 지역변수의 유효범위는 선언된 지점부터 해당 블록 끝이다.

- 거의 모든 지역 변수 선언에는 초기값이 포함되어야 한다.

- 메서드의 크기를 줄이고 특정한 기능에 집중하라.


2. 순환문(Loop)을 이용한 지역변수 최소화

- while문 보다는 for문을 사용하여 유효범위를 최소화 할 수 있다.

- 순환변수를 사용하여 순환문안에 지역변수를 최소화 한다.

1
2
3
for(int i=0, n = expensiveComputation; i<n; i++){
  doSomething(i);
}

- n, i 유효범위가 for문 안으로 제한된다. 

블로그 이미지

리딩리드

,