본문 바로가기
Effective JAVA 2판

Effective JAVA Item15 변경 가능성을 최소화 하라

by BroBroBro 2015. 5. 9.

------------------------------------------------

본글은 Effective JAVA 2판의 책의 내용을 토대로 작성하였으며
지극히 주관적인 사항들이 많이 있으므로 가려서 읽으시기바랍니다.
잘못된 내용이 있거나 의견이 있으시다면 언제든 댓글로^^~!

------------------------------------------------

CHAPTER 4 클래스와 인터페이스
4장에서는 클래스와 인터페이스를 설계할때 이용할 수 있는 강력한 요소들을 많이 갖추고 있다고 한다. 
[Item 13] 클래스와 그 멤버의 접근 권한을 최소화하자 부터 보도록 하자 
Item14 public 클래스 안에는 public 필드를 두지말고 접근자(accessor) 메소드를 사용한라
Item15 변경 기능성을 최소화 하라 
Item16 계승하는 대신 구성하라
Item17 계승을 위한 설계와 문서를 갖추거나, 그럴 수 없다면 계승을 금지하라
Item18 추상 클래스 대신 인터페이스를 사용하라
Item19 인터페이스는 자료형을 정의할 때만 사용하라
Item20 태그 달린 클래스 대신 클래스 계층을 활용하라
Item21 전략을 표현하고 싶을 때는 함수 객체를 사용하라
Item22 멤버 클래스는 가능하면 static으로 선언하라.

--page1 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

--page1 

본장에서는 변경 불가능 클래스를 만들라고 하며 그이유에 대한 5가지 규칙을 설명하고있다.

1번 객체 상태를 변경하는 메서드(수정자 mutator메서드)를 제공하지 않는다.
2번 계승할 수 없도록 한다.
3번 모든 필드를 final로 선언한다.
4번 모든 필드를 private로 선언한다.
5번 변경 가능 컴포넌트에 대한 독점적 접근권을 보장한다.

왜 변경 불가능 클래스로 만드냐는 질문에는 변경 가능 클래스보다

 1) 설계하기 쉽고,

 2)구현하기쉽고,

 3) 사용하기도 쉽다고 한다.

 그럼 5가지 규칙에대해서 살펴보도록 하자.


1번 객체 상태를 변경하는 메서드(수정자 mutator메서드)를 제공하지 않는다.
이는 우리가 흔히 알고있는 set으로 시작하는 setter메서드이다.

2번 계승할 수 없도록 한다.
이도 우리가 알고있는 final키워드로 하면된다. 

3번 모든 필드를 final로 선언한다.
final을 붙이면 맨처음에 한번만 초기화 할수있고 변경이 불가능하다. 이도 우리가 알고있는내용이며 책에서는 이런 말을 하고있다. " 새로 생성된 객체에 대한 참조가 동기화(synchronization)없이 다른 스레드로 전달되어도 안전하다. 라고 여기서 막히는 분들은 그냥 " 변경을 불가능하게 하면 스레드에서 사용시에 문제가 없으며 safe하다" 정도로 이해하자.(사실 나도 스레드를 잘안사용해봐서 모른다..^^;, 하지만 mutable imutalbe의 의미가 왜 언어에서 중요한지 는 알기에..., 나중에 반드시 다룰거다.)

4번 모든 필드를 private로 선언한다.
set도 제공안하고 private로 선언하면 외부에서 변경이 안되겠죠? 13장에서 public static final로 변경 불가능(immutable)을 기술적으로 허용하지만 추천하지는 않는다고 한다.

5번 변경 가능 컴포넌트에 대한 독점적 접근권을 보장한다.
이건 나도 잘모르겠다 근데 말그대로 해석하면 , 최소화 하지만 최소활할수없어서 변경 가능하게 만들었다면 클라이언트가 접근시에 독점적으로 접근하게 해라, 즉 lock걸고 해당 객체를 써라 정도로 이해를 했다. 이또한 스레드와 연관이 있어보인다.


--page2

복소수(complex number, 즉 실수부와 허수부를 갖는 수) 를 표현하는 클래스를 책에서 설명한다....이런...복소수가 뭔지 우선 탐구하자...ㅠㅠ


--------여기서부터는 안읽어도 됨. 복소수에 관한이야기입니다.

a+bi 가있을때 a를 실수 b는 실수 i는 허수라고 한다.

페르마? x의n제곱 y의n제곱 이 z의n제곱 일때 n=2는 만족하지만 3이상일때를 만족하는 수가 없다....300백년동안 안풀렸다고하는데요..ㅋㅋ 오일러가 조금 접근하다가 "허수"라는걸 확립했구요..그전에 으아아악..뭔소리야 머리아프게..아무튼 데카르트, 카르다노 등등이 개념을 잡았었드랬죠!!!

허수란 뭐죠? 제곱해서 음수가 되는수입니다. 아!!!! 쉽다 ㅋㅋㅋ

오호 수식입력기가 있네 한번 해보죠..

아...뭔가 어렵네...


다시다시 책에서도 말하고 있다. 복소수는 실수부와 허수부를 같는 수라고 한다? 실수부는 뭐고 허수부는 뭔가?

a 가 실수부이고 b가 허수부이다 i는 허수이다. 복소수는 a+bi 이며 이공식을 가지고  기존에 수의 개념에서 완전 확장되어 보다 세밀한 표현이 , 수의 범위의 표현가능해졌다.

즉 대빵 큰수가 아니라 대빵 넓은 ,인간이 눈으로 볼수없는 엄청나게 대단한 수를 표현 가능하다는 것이다.

"수의 가장 확장된 개념" = 복소수 = complex number 임.

아...복소수 알아보다가..다른 동영상 클릭해서 2시간 날려먹었네..졸리다...항상 졸려~!

--------여기서부터는 다시 읽으세요.


복소수를 표현한 예제를 확인해봅시다.(좀길어요^^;)

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// Immutable class - pages 76-78
package org.effectivejava.examples.chapter04.item15;
 
public final class Complex {
    private final double re;
    private final double im;
 
    private Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }
 
    public static Complex valueOf(double re, double im) {
        return new Complex(re, im);
    }
 
    public static Complex valueOfPolar(double r, double theta) {
        return new Complex(r * Math.cos(theta), r * Math.sin(theta));
    }
 
    public static final Complex ZERO = new Complex(00);
    public static final Complex ONE = new Complex(10);
    public static final Complex I = new Complex(01);
 
    // Accessors with no corresponding mutators
    public double realPart() {
        return re;
    }
 
    public double imaginaryPart() {
        return im;
    }
 
    public Complex add(Complex c) {
        return new Complex(re + c.re, im + c.im);
    }
 
    public Complex subtract(Complex c) {
        return new Complex(re - c.re, im - c.im);
    }
 
    public Complex multiply(Complex c) {
        return new Complex(re * c.re - im * c.im, re * c.im + im * c.re);
    }
 
    public Complex divide(Complex c) {
        double tmp = c.re * c.re + c.im * c.im;
        return new Complex((re * c.re + im * c.im) / tmp, (im * c.re - re
                * c.im)
                / tmp);
    }
 
    @Override
    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof Complex))
            return false;
        Complex c = (Complex) o;
 
        // See page 43 to find out why we use compare instead of ==
        return Double.compare(re, c.re) == 0 && Double.compare(im, c.im) == 0;
    }
 
    @Override
    public int hashCode() {
        int result = 17 + hashDouble(re);
        result = 31 * result + hashDouble(im);
        return result;
    }
 
    private int hashDouble(double val) {
        long longBits = Double.doubleToLongBits(re);
        return (int) (longBits ^ (longBits >>> 32));
    }
 
    @Override
    public String toString() {
        return "(" + re + " + " + im + "i)";
    }
}
cs


34~51줄까지 사칙연산의 메소드가 보입니다. 모두 새로운 Complex객체를 반환하고 있습니다. 책에서는 이런말을 하고 있습니다. 함수형 접근법(functional approach)으로도 알려져 있는데, 피연산자를 변경하는 대신, 연산을 적용한 결과를 새롭게 만들어 반환한다고... 또한 절차적(procedural)또는 명령형(imperative)접근법은 피연산자에 일정한 절차를 적용하여 그 상태를 바꾼다고.. 

앗, 함수에서 새로운 객체를 return하면 그게 함수형 접근법이라고 하네요^^; 이런 JAVA는 함수형언어가 아닌줄알고있었는데...아무튼..

여기서 강조하는것은 새로운 객체를 생성하기때문에 변경불가능 하다는 것입니다. 즉 생성자들이 불변식(invariant)를 확실히 따르고 있다는것이죠.


계속 말했지만 변경불가능한것은 스레드에 안전(thread-safe)하며, 어떠한 동기화도 필요없습니다. 스레드가 동시에 사용해도 훼손될우려가 없습죠^^;


또한 변경불가능하기때문에 위에서

  public static final Complex ZERO 

등이 있죠? 이렇게 마구마구 제공해줘도 된다는거죠!!!

책에서 계속 변경불가능에대해서 장점들을 말하고있는데 아래와 같습니다.

- 내부도 공유할수있다. , BigInteger클래스에서 부호만 바꾸어서 원래 객체의 내부배열을 참조할수있다.

- 다른 객체의 구성요소로도 훌륭하다, 우리가 흔히 알고있는 map 과 set을 들수있겠죠.

- 유일한 단점은 값마다 별도의 객체를 만들어야 한다는 점이다., 앗 단점을 말하고있네요? 이게 뭘까요?

BigInteger에는 flipBit라는 메소드가 있습니다. 원래의 값에서 딱한개의 비트만을 변경한다고한다.
만약에 백만개라면? 새로운객체를 생성해서 반환하지만 비용이 너무 높다. !!!

이에 대한 해법도있다고 한다. companion class로 해결할수있다고 한다.(example. String class에 대한 StringBuiler)
(이부분에 대해서는 나중에 다시한번 책을 읽을때 확인해보자, 오늘도 역시 졸려서 그만 마무리 하련다...)


뭔가 복잡해졌다. 다시 위의 소스로 돌아가자

이책에서 말하고 있는 변경가능성을 최소화 하려면 모든 생성자를 private나 package-private(아무것도 안붙이는것)로 선언하고 public 생성자 대신 정적 팩토리를 제공하면 된다. (끝 ㅋㅋㅋ) 아참 추가 특별한 이유가 없다면 모든 필드는 위 예제 처럼 final로 선언하면 된다.!!