본문 바로가기
Language/Effective java

일반적인 프로그래밍 원칙

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

리플렉션보다는 인터페이스를 사용하라.

자바 리플렉션(java.lang.reflect)을 이용하면 프로그램에서 임의의 클래스에 접근할 수 있다. 리플렉션은 Class, Method, Field 인스턴스를 가지고 올 수 있으며 조작 할 수도 있다. 컴파일 당시에 존재하지 않던 클래스도 이용 할 수 있다.

 

리플렉션의 단점

  • 컴파일타임 타입 검사가 주는 이점을 하나도 누릴 수 없다. 존재하지 않거나 접근 할 수 없는 메서드를 호출하려 시도하면 런타임 오류가 발생한다.
  • 리플렉션을 이용하면 코드가 지저분하고 장황해진다. 
  • 성능이 떨어진다. 리플렉션을 통한 메서드 호출은 일반 메서드 호출보다 훨씬 느리다.

 

리플렉션이 필요한지 확실할 수 없다면 필요없을 가능성이 크다. 리플렉션은 아주 제한된 형태로만 사용해야 그 단점을 피하고 이점을 취할 수 있다. 대부분은 상위클래스나 적절한 인터페이스를 활용하여 문제를 해결 할 수 있다.

 

 

아래코드는 두가지 단점이 있다.

  • 런타임에 총 여섯가지나 되는 예외를 던질 수 있다. 하지만, 리플렉션이 아니라면 컴파일 타임에 다 잡을 수 있다. 리플렉션은 런타임에러가 난다.
  • 클래스 이름만으로 인스턴스를 생성해내기 위해 무려 25줄이나 되는 코드를 작성했다. 
public class Main {
    public static void main(String[] args) {
        
        Class<? extends Set<String>> cl = null;
        try {
            cl = (Class<? extends Set<String>>) Class.forName(args[0]);
        } catch (ClassNotFoundException e) {
            fatalError("클래스를 찾을 수 없습니다.");
        }

        Constructor<? extends Set<String>> cons = null;
        try {
            cons = cl.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
            fatalError("매개변수 없는 생성자를 찾을 수 없습니다.");
        }
        
        Set<String> s = null;
        try {
            s = cons.newInstance();
        } catch (IllegalAccessException e) {
            fatalError("생성자에 접근할 수 없습니다.");
        } catch (InstantiationException e) {
            fatalError("클래스를 인스턴스화할 수 없습니다.");
        } catch (ClassCastException e) {
            fatalError("Set을 구현하지 않은 클래스입니다.");
        } catch (RuntimeException e) {
            fatalError("runtime");
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }

        s.addAll(Arrays.asList(args).subList(1, args.length));
        System.out.println(s);
    }
    
    private static void fatalError(String msg) {
        System.err.println(msg);
        System.exit(1);
    }
}

 

리플렉션의 사용하는 시점

  • 리플렉션은 그런일은 거의 없지만 런타임에 존재하지 않을 수도 있는 다른 클래스, 메서드, 필드와의 의존성을 관리할 때 적합하다.
  • 버전이 여러개 존재하는 외부 패키지를 다룰 때 유용하다. 예전 버전을 컴파일 한 후 이후 버전의 클래스와 메서드를 접근 할 때 사용한다.

 

네이티브 메서드는 신중히 사용하라.

자바 네이티브 인터페이스(Java Native Interface, JNI)는 자바 프로그램이 네이티브 메서드를 호출하는 기술이다. 네이티브 메서드란 C나 C++같은 네티이브 프로그래밍 언어로 작성한 메서드를 말한다.

 

네이티브 메서드의 쓰임새

  • 레지스트리 같은 플랫폼 특화 기능을 사용한다.
  • 네이티브 코드로 작성된 기존 라이브러리를 사용한다.
  • 성능 개선을 목적으로 성능에 결정적인 영향을 주는 영역만 따로 네이티브 언어로 작성한다.

 

성능을 개선할 목적으로 네이티브 메서드를 사용하는 것은 거의 권장하지 않는다. 

  • 네이티브 언어가 안전하지 않으므로 네이티브 메서드를 사용하는 애플르케이션도 메모리 훼손 오류로부터 더이상 안전하지 않다. 
  • 디버깅이 어렵다.
  • 속도가 오히려 느려질 수도 있다.
  • 가비지 컬렉터가 네이티브 메모리를 자동으로 회수하지 못하고, 심지어 추적조차 할 수 없다.
  • 네이티브와 자바 코드사이 접착 코드를 작성해야 한다.

하지만, 고성능 다중 정밀 연산이 필요한 자바 프로그래머라면 네이티브 메서드를 통해 GMP를 사용해도 된다.

 

최적화는 신중히 하라.

그 어떤 핑계보다 효율성이라는 이름 아래 행해진 컴퓨팅 죄악이 더 많다. - 윌리엄 울프

(전체의 97% 정도인) 자그마한 효율성은 모두 잊자. 섣부른 최적화가 만악의 근원이다 - 도널드 크누스

최적화 할때는 다음 두 규칙을 따르라.
첫 번째, 하지마라.
두 번째, (전문가 한정) 아직 하지 마라.
다시 말해, 완전히 명백하고 최적화되지 않은 해법을 찾을 때까지는 하지마라. - M.A 잭슨

 

빠른 프로그램보다는 좋은 프로그램을 작성하라.

  • 좋은 프로그램이지만 원하는 성능이 나오지 않는다면 그 아키텍처 자체가 최적화할 수 있는 길을 안내해줄 것이다.
  • 좋은 프로그램은 정보 은닉 원칙을 따르므로 개별 구성요소의 내부를 독립적으로 설계 할 수 있다.
  • 구현상의 문제는 나중에 최적화해 해결할 수 있지만, 아키텍처의 결함이 성능을 제한하는 상황이라면 전체를 다시 작성하지 않고는 해결하기 불가능할 수 있다.

 

성능을 제한하는 설계를 피하라.

  • 완성 후 가장 변경하기가 어려운 설계 요소는 바로 컴포넌트사이와 외부 시스템과의 소통 방식이다.

 

API를 설계할 때 성능에 주는 영향을 고려하라.

  • public타입을 가변으로 만들면 불필요한 방어적 복사를 수 없이 유발할 수 있다.
  • 하위 타입인 경우 상위 타입의 성능의 제약도 물려받는다.
  • 인터페이스가 있음에도 구현체를 사용하는 것은 좋지 않다. 나중에 더 좋은 구현체가 나왔을 때 활용하기 어렵다.

 

잘 설계된 API는 성능도 보통 좋기 때문에 성능을 위해 API를 왜곡하는 건 매우 좋지 않다.

 

프로파일링 도구를 통해 최적화 노력을 어디에 집중해야 할지 찾는 데 도움을 준다.

  • 90%의 시간을 단 10%의 코드에서 사용한다. 
  • 이 도구는 개별 메서비드의 소비 시간과 호출 횟수 같은 런타임 정보를 제공하여 집중할 곳을 알려준다.
  • 그 부분의 알고리즘을 교체한다면 다른 튜닝을 하지 않아도 문제는 사라질 것이다.

 

소스코드와 CPU간의 추상화 격차가 커서 최적화 인한 성능 변화를 일정하게 예측하기 어렵다. 자바의 성능 모델은 정교하지 않을뿐더러 구현 시스템, 릴리스, 프로세서마다 차이가 있다.

 

일반적으로 통용되는 명명 규칙을 따르라.

자바 플랫폼은 명명 규칙이 잘 정립되어 있으며 그 중 많은 것이 자바 언어 명세에 기술되어 있다.

 

철자 규칙

식별자 타입
패키지와 모듈 org.junit.jupiter.api, com.google.common.collect
클래스와 인터페이스 Stream, FutrueTask, LinkedHashMap, HttpClient
메서드와 필드 remove, groupingBy, getCrc
상수 필드 MIN_VALUE, NEGATIVE_INFINITY
지역변수 i, demon, houseNum
타입 매개변수 T, E, K, V, X, R, U, V, T1, T2

 

문법 규칙

구분 규칙
객체를 생성할 수 있는 클래스 단수 명사나 명사구  Thread, PriorityQueue, ChessPiece
객체를 생성할 수 없는 클래스 복수형 명사 Collectors, Collections
인터페이스 이름 able 혹은 ible로 끝나는 형용사 Runnable, Iterable, Accessible
에너터이션 명사, 동사, 전치사, 형용사 두루두루 BindingAnnotation, Inject 
동작을 수행하는 메서드 동사나 동사구 append, drawImage
boolean 반환 타입 is, has 시작한다. isDigit, hasSiblings
객체 타입을 바꾸는 메서드 toType형태 toString, toArray
객체의 내용을 다른 뷰로 바꿀 때 asType형태 asList
정적팩터리 참고

 

 

 

728x90

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

예외  (0) 2022.08.18
예외  (0) 2022.08.17
일반적인 프로그래밍 원칙  (0) 2022.08.11
일반적인 프로그래밍 원칙  (0) 2022.08.10
일반적인 프로그래밍 원칙  (0) 2022.08.08

댓글