본문 바로가기
Language/Effective java

일반적인 프로그래밍 원칙

by y.j 2022. 8. 8.
728x90

지역변수의 범위를 최소화하라.

지역변수의 유효범위를 최소로 줄이면 코드 가독성과 유지보수성이 높아지고 오류 가능성은 낮아진다.

 

지역변수의 범위를 줄이는 가장 강력한 기법은 '가장 처음 쓰일 때 선언하기'이다.

 - 사용하기 너무 앞서 선언해주면 코드가 어수선해져 가독성이 떨어진다.

   변수를 실제로 사용하는 시점엔 타입과 초깃값이 기억나지 않을 수도 있다.

   또, 실제 사용하는 블록 바깥에 선언된 변수는 그 블록이 끝나 ㄴ뒤까지 살아있게 되어 끔찍한 결과로 이어질 수 있다.

 

거의 모든 지역변수는 선언과 동시에 초기화해야 한다.

 - 초기화에 필요한 정보가 충분하지 않다면 충분해질 때까지 선언을 미뤄야 한다.

    try-catch문은 이 규칙에서 예외다. 변수를 초기화하는 표현식에서 검사 예외를 던질 가능성이 있다면

    try 블록 안에서 초기화해야 한다. 한편, 변수 값을 try블록 바깥에서도 사용해야 한다면 try블록 앞에서 선언해야 한다.

 

while문보다 for문이 나은 이유

for문

  • for문은 지역변수를 반복문의 내부에 선언하여 변수 범위를 최소화 한다.
  • 반복문의 변수 값을 블록 외부에서 써야 할 상황이 아니라면 for문이 장점이 더 많다.
for(Element e : c) {
    ... // e로 무언가를 한다.
}
for(Iterator<Element> i = c.iterator(); i.hasNext(); ) {
    Element e = i.next();
    ... // e와 i로 무언가를 한다.
}
  • n이라는 지역변수에 저장하여 메서드를 여러번 호출 할 필요가 없다.
for(int i = 0, n = expensiveComputation(); i < n; i++) {
    ... // i로 무언가를 한다.
}

 

while문

while문의 경우에는 2번째 while문에 i2대신 i로 잘못쓴거처럼 버그를 만들 수도 있다. 이 코드는 컴파일도 잘되고 실행시 예외를 던지지도 않는다. 하지만 for 문을 사용하면 i의 유효범위가 끝났기 때문에 컴파일에러를 낼 것이다.

Iterator<Element> i = c.iterator();
while(i.hasNext()) {
    doSomething(i.next());
}
...

Iterator<Element> i2 = c2.iterator();
while(i.hasNext()) {
    doSomething(i2.next())
}

 

메서드를 작게 유지하고 한가지 기능에 집중하라.

 - 여러 가지 기능을 처리한다면 지역 변수(인자로 삼거나..)를 다른 기능을 수행하는 코드에서 접근 할 수 있다.

 

전통적인 for문보다는 for-each문을 사용하라

스트림에 제격인 작업이 있고 반복이 제격인 작업이 있다.

 

전통적인 방법

  • 반복자와 인덱스 변수는 코드를 지저분하게 할 뿐 우리에게 진짜 필요한 건 원소들 뿐이다.
  • 아래 코드들은 변수를 헷갈려 잘못 쓸 가능성이 충분히 있다.
for(Iterator<Element> i = c.iterator(); i.hasNext(); ) {
    Element e = i.next();
    ... // e로 무언가를 한다.
}
for(int i = 0; i < a.length; i++) {
    ... // a[i]로 무언가를 한다.
}

 

향상된 for 문 (enhanced for statement; for-each문)

아래코드는 c내에 있는 각 Element e에 대해라고 읽는다.

for(Element e : c) {
    ... // e로 무언가를 한다.
}

 

for-each의 장점

아래코드는 버그가 있다. Suit하나당 Rank가 들어있어야 Rank하나당 한번씩 불리고 있다.

enum Suit{ CLUB, DIAMOND, HEART, SPADE }
enum Rank{ ACE, DEUCE, FOUR, FIVE, SIX, SEVEN, EIGHT,
           NINE, TEN, JACK, QUEEN, KING }
...

static Collection<Suit> suits = Arrays.asList(Suit.values());
static Collection<Suit> ranks = Arrays.asList(Rank.values());

List<Card> deck = new ArrayList<>();
for(Iterator<Suit> i = suits.iterator(); i.hasNext(); )
    for(Iterator<Rank> j = ranks.iterator(); j.hasNext(); )
        deck.add(new Card(i.next(), j.next()));

아래로 바꾸면 헷갈리지 않을 수 있다.

for(Suit suit : suits) 
    for (Rank rank : ranks )
        deck.add(new Card(suit, rank));

 

for-each문을 사용할 수 없는 상황

  • 파괴적인 필터링 : 컬렉션을 순회하면서 선택된 원소를 제거해야 하는 경우
  • 변형 : 리스트나 배열을 순회하면서 그 원소으 값 일부 혹은 전체를 교체해야 하는 경우 ( 인덱스를 사용해야 하기 때문 )
  • 병렬 반복 : 여러 컬렉션을 병렬로 순회할 때 각각의 반복자와 인덱스 변수를 사용해 엄격하고 명시적으로 제어해야 한다.

 

라이브러리를 익히고 사용하라.

 

표준 라이브러리를 사용하면 그 코드를 작성한 전문가의 지식과 여러분보다 앞서 사용한 다른 프로그래머들의 경험을 활용할 수 있다.

 

아래코드는 3가지의 문제점이 있다.

static Random rnd = new Random();

static int random(int n) {
    return Math.abs(rnd.nextInt());
}
  • n이 그리 크지 않은 2의 제곱수라면 얼마 지나지 않아 같은 수열이 반복된다.
  • n이 2의 제곱수가 아니라면 몇몇 숫자가 평균적으로 더 자주 반환된다.
  • n값이 크면 이 현상은 더 두드러진다.

 

public static void main(String[] args) {
    int n = 2 * (Integer.MAX_VALUE / 3);
    int low = 0;
    for(int i = 0; i < 1000000; i++)
        if(random(n) < n / 2)
            low++;
    System.out.println(low);
}

위 코드를 돌렸을 때 랜덤으로 실행했기 때문에 50만개에 가까운 출력은 해야 하지만 666,666에 가까운 숫자를 내보내게 된다.중심에서 낮은 쪽이나 높은쪽으로 쏠린 것이다.

 

다행히 Random.nextInt(int)가 잘 해결해 놓았다. 이것을 해결하기 위해서는 정수론, 2의 보수 계산 등에 조예가 깊어야 한다. 

Java7이후로부터는 ThreadLocalRandom으로 대체하면 고품질이며 빠른 난수를 기대 할 수 있다. 포크-조인 풀이나 병렬 스트림에서는 SplittableRandom을 사용하는 것이 좋다.

 

핵심적인 일과 크게 관련 없는 문제를 해결하느라 시간을 허비하지 않아도 된다.

애플리케이션 개발에 더 집중 할 수 있게 된다.

 

이점은 따로 노력하지 않아도 성능이 지속해서 개선된다는 점이다.

사용자가 많고, 업계 표준 벤치마크를 사용해 성능을 확인하기 때문에 표준 라이브러리 제작자들은 더 나은 방법을 꾸준히 모색할 수밖에 없다.

 

기능이 점점 많아 진다.

라이브러리에 부족한 부분이 있다면 커뮤니티에서 이야기가 나오고 논의된 후 다음 릴리스에 해당 기능이 추가되곤 한다.

 

메이저 릴리스마다 주목할 만한 수 많은 기능이 라이브러리에 추가된다.

 

자바 프로그래머라면 적어도 java.lang, java.util, java.io와 그 하위 패키지들에는 익숙해져야 한다.

java.util.concurrent도 잘 쓸 수 있도록 하자.

 

728x90

'Language > Effective java' 카테고리의 다른 글

일반적인 프로그래밍 원칙  (0) 2022.08.11
일반적인 프로그래밍 원칙  (0) 2022.08.10
메서드  (0) 2022.08.04
메서드  (0) 2022.08.01
메서드  (0) 2022.08.01

댓글