목 차
1. Java Language 규칙
A. 예외를 무시하지 마라
B. 제네릭 예외를 처리하지 마라
C. Finalizers를 이용하지 마라
D. Imports 문을 축약하지 마라
2. Java 스타일 규칙
A. Javadoc 표준을 준수한다
B. 메소드를 짧게 작성한다
C. 멤버변수는 정해진 위치에 선언한다
D. 변수의 스코프는 최소화 한다
E. Import 문의 순서에 신경 쓴다
F. Tab 대신 Space로 들여쓰기를 한다
G. 멤버 변수명 규칙
H. 표준 중괄호 스타일을 따른다
I. 기타
3. 로그에 대해서
4. Java Test 스타일 규칙
원문은 여기에서 확인할 수 있다. 이 코딩 스타일 가이드라인은 안드로이드 오픈소스 컨트리뷰터를 위한 가이드라인이다.
앱을 개발할 때에도 참조할 수 있는 내용일 것 같아서 정리해본다.
1. Java Language 규칙
A. 예외를 무시하지 마라
이전 포스팅에도 언급된 내용이다. 아래와 같이 예외를 무시하지 말라는 이야기.
void setServerPort(String value) { try { serverPort = Integer.parseInt(value); } catch (NumberFormatException e) { } }
이후에 이 코드를 볼 사람을 위해서라도 예외처리하던지, 주석이라도 달아놓자. 이후에 위의 코드를 보는 사람은 분명히 이상하게 생각할 수 밖에 없다. 예외처리를 하지 않아도 되는 예외 케이스도 존재한다.
예외를 아예 처리하지 않는다 ⇒ 상위 호출자에게 예외를 전달
예외를 잡아서 다른 예외로 바꾸어 전달
예외를 무시할 만한 명확한 이유가 있을 때, 충분한 주석을 단 이후 무시
예외 발생시 디폴트 설정을 사용하는 식의 처리. 아래 예 확인
/** Set port. If value is not a valid number, 80 is substituted. */ void setServerPort(String value) { try { serverPort = Integer.parseInt(value); } catch (NumberFormatException e) { serverPort = 80; // default port for server } }
B. 제네릭 예외를 처리하지 마라
예외 또한 상속 계층으로 이뤄져 있다. 최상위 혹은 상위 레벨의 예외로 모든 예외를 처리하려고 하지 말라는 의미다. 사실 구현의 편리함 때문에 아래와 같은 코드는 쉽게 찾아볼 수 있다.
try { someComplicatedIOFunction(); // may throw IOException someComplicatedParsingFunction(); // may throw ParsingException someComplicatedSecurityFunction(); // may throw SecurityException // phew, made it all the way } catch (Exception e) { // I'll just catch all exceptions handleError(); // with one generic handler! }
상위 레벨의 예외를 처리함으로써 실제로는 예상치도 못한 예외 상황까지 handleError에 넘어가게 된다. 당연히 해당 메소드는 정상적으로 예외 처리를 할 수 없다. 또한 향후에 하위 메소드에서 다른 예외가 추가되더라도 이를 적절히 처리해줄 수 없게 된다. 정리하면 예외처리는 최대한 디테일(하위 클래스 레벨) 한 레벨에서 처리해라. 제네릭 예외를 꼭 처리해야 하는 상황이면 충분히 주석을 달아서 이 타당성을 설명한다.
제네릭 예외를 잡는 코드는 다음과 같은 방법을 통해서 해결한다.
예외를 처리하지 않고 상위 레이어로 전달한다.
여러 catch 블락을 통해서 하나하나 세부적으로 핸들링한다.
try-catch 문을 여러 개 만들어서 하나 하나 처리한다.
C. Finalizers를 이용하지 마라
이전 포스팅에도 언급된 내용이다. 이전 포스팅을 확인한다. 요약하자면 close 같은 메소드를 만들어 객체 내에서 종료 상태를 따로 관리하라고 한다. 그 이유는 Finalizer의 경우 성능에 문제를 가지고 있으며 언제 호출될 지 컨트롤 할 수 없기 때문이다.
D. Imports 문을 축약하지 마라
이것도 이전 포스팅에도 언급된 내용이다. 간단히 import 문에 스타(*)를 사용하지 말라는 뜻이다.
import foo.*; // 틀린 방식
import foo.Bar; // 맞는 방식
아래와 같은 케이스에서는 예외를 허용하고 있다.
java 표준 라이브러리 import의 경우 (java.util.* / java.io.*)
unit test 코드의 경우 (junit.framework.*)
2. Java 스타일 규칙
A. Javadoc 표준을 준수한다
아래는 잘 작성된 java 파일의 예이다.
/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.foo; import android.os.Blah; import android.view.Yada; import java.sql.ResultSet; import java.sql.SQLException; /** * Does X and Y and provides an abstraction for Z. */ public class Foo { ... }
모든 파일의 첫 부분에는 라이센스가 명시되어야 한다.
package 선과 import 선언이 순서대로 기술된다.
그리고 Javadoc 주석으로 클래스나 인터페이스의 역할에 대해서 설명한다.
public 메소드는 Javadoc 형태의 설명이 필수적으로 붙어야 한다.
getter / setter 같은 사소한 메소드에는 Javadoc 주석을 추가하지 않아도 된다.
B. 메소드를 짧게 작성한다
메소드는 최대한 작고 하나의 일에 집중하도록 구현되어야 유지보수에 좋다. 절대적인 법칙은 없지만 40문장을 넘어가면 메소드추출 등을 통해서 줄일 수 있는 방법이 있는지 고민하자.
C. 멤버변수는 정해진 위치에 선언한다.
두 군데 위치가 있다.
클래스의 선언 바로 다음
해당 멤버변수가 사용되기 바로 직전 메소드 위
D. 변수의 스코프는 최소화 한다.
로컬 변수의 스코프를 최소화하라. 사용되기 직전에 선언됨으로써 가독성을 높일 뿐만 아니라 예상치 못한 위치에서 사용되는 사태를 막아 유지보수도 유리하다. 변수를 선언과 동시에 초기화할 수 없다면 변수의 선언이 너무 빠른 것이 아닌지 의심해봐야 한다.
아래와 같은 상황을 생각해볼 수 있다. 이 경우는 예외로 볼 수 있는데 s 변수를 try-catch 절 외부에서 사용하기 때문에 try-catch 절 내에서는 선언할 수 없다.
// Instantiate class cl, which represents some sort of Set Set s = null; try { s = (Set) cl.newInstance(); } catch(IllegalAccessException e) { throw new IllegalArgumentException(cl + " not accessible"); } catch(InstantiationException e) { throw new IllegalArgumentException(cl + " not instantiable"); } // Exercise the set s.addAll(Arrays.asList(args));
하지만 이런 같은 경우에도 try-catch 절 자체를 함수로 만들게 되면 우회 할 수 있다.
Set createSet(Class cl) { // Instantiate class cl, which represents some sort of Set try { return (Set) cl.newInstance(); } catch(IllegalAccessException e) { throw new IllegalArgumentException(cl + " not accessible"); } catch(InstantiationException e) { throw new IllegalArgumentException(cl + " not instantiable"); } } ... // Exercise the set Set s = createSet(cl); s.addAll(Arrays.asList(args));
E. Import 문의 순서에 신경 쓴다
import 문의 그룹핑 순서는 아래와 같다.
Android import
3rd party import (com, junit, net, org)
java and javax
각 그룹은 알파벳 순으로 (ASCII 코드 순 - 따라서 대문자가 소문자에 우선한다) 정렬되어 있어야 한다. 그리고 각 그룹핑 사이에는 빈 공백 라인이 한 줄 들어가야 한다.
이런 순서를 결정한 이유는 개발자가 가장 중요하게 생각하는 것을 처음에 두는 경향이 있기 때문이다. IDE가 자동으로 이런 순서는 정리해주니까 중요하게 생각하지는 말자.
F. Tab 대신 Space로 들여쓰기를 한다
구글 자바 코딩 규약과는 다르게 4개의 스페이스를 들여쓰기로 사용하고 있다. 마찬가지로 탭의 사용을 금한다. line wrap에서는 8개의 스페이스를 사용한다. (line wrap은 코드가 길어서 뒤 부분을 다음 문장으로 내리는 액션을 의미한다)
G. 멤버 변수명 규칙
이것도 구글 자바 코딩 규약과는 차이를 가진다. 거기서는 어떠한 prefix / suffixes를 허용하지 않았는데 여기서는 사용한다.
public class MyClass { public static final int SOME_CONSTANT = 42; public int publicField; private static MyClass sSingleton; int mPackagePrivate; private int mPrivate; protected int mProtected; }
public과 static이 아닌 멤버 변수는 m으로 시작한다.
static의 경우는 s로 시작한다.
public static final (상수)는 모두 대문자로 쓰고 단어 사이는 밑줄로 처리한다.
public member의 경우 다 소문자로 작성한다.
H. 표준 중괄호 스타일을 따른다
표준 중괄호 스타일은 구글 자바 코딩 규약을 살펴보자. 예제는 다음과 같다. 기본적으로 시작 중괄호를 내리지 않고 닫는 중괄호 다음에 코드를 연결해서 작성한다고 보면 된다.
class MyClass { int func() { if (something) { // ... } else if (somethingElse) { // ... } else { // ... } } }
중괄호의 경우 생략하지 않는다. 코드가 한 줄이라도 중괄호는 꼭 써준다.
코드가 짧다면 중괄호를 포함해서 한 줄로 만드는 것은 상관없다.
I. 기타
라인의 길이는 100문자를 넘어가지 않는다. (URL이나 package, import 문은 예외)
Annotation은 알파벳 순서로 한 줄에 하나씩 선언한다.
@Deprecated : 당장은 사용 가능하더라도 향후 제거 예정인 메소드에 붙는 annotation, 이 annotation을 쓰려면 대신 사용할 수 있는 방법에 대해서 Javadoc으로 설명을 해야 한다.
@Override : 상위 클래스의 메소드를 재정의 할 때나 인터페이스를 구현할 때 무조건 빼지 않고 기술해야 한다.
@SuppressWarnings : Warning 메시지를 제거할 수 없을 때 사용되는 Annotation 이다. 실제 warning과 구별하기 위해서 사용한다. 이 Annotation을 사용할 때는 TODO 주석으로 그 이유를 설명하며 같이 사용한다.
변수나 메소드 선언시 약자를 하나의 단어로 취급한다. 가령 XML은 틀린 표기법이고 Xml을 사용해야 한다.
임시코드나 단기해결책 등 향후에 고쳐질 가능성이 있는 부분에는 TODO로 주석을 단다. TODO 다음에는 콜론이 붙어서 나온다. // TODO: 이런식이다.
3. 로그에 대해서
로깅 자체는 필수적이기는 하지만 성능에 악영향을 미친다. 따라서 필요한 로그를 최소한으로 유지할 필요가 있다. 안드로이드에서는 아래 5개의 로그 레벨일 제공한다. 코딩 스타일 이야기 외에도 로그에 대해서 조금 상세히 설명하고 있었는데 이 부분은 향후에 실제 코드와 함께 포스팅해볼 예정이다.
ERROR: 정말 심각한 문제와 관련된 로그레벨이다. 복구 불가능한 문제이다.
WARNING: 예상치 못한 문제가 발생했으나 복구 가능한 문제이다. (앱 재시작, 파일 재 다운로드, 시스템 재부팅, 유저에게 input 요청 등)
INFORMATIVE: 문제는 아니지만 관심가질 만한 정보를 출력하는 로그 레벨이다.
DEBUG: 추가적인 정보를 얻기 위한 레벨이다. 컴포넌트 디버그 혹은 상세 조사등에 사용되는 로그레벨이다. 로깅 자체가 성능을 떨어트리기 때문에 if (LOCAL_LOG) 혹은 if (LCAL_LOGD) 같은 문으로 전체가 쉽게 disable 될 수 있는 형태여야 한다.
VERBOSE: 그 외 모든 로그가 위치하는 레벨이다. 디버그 빌드에서만 활성화되고 릴리즈 빌드에서는 자동으로 제거된다.
4. Java Test 스타일 규칙
test<MethodName>_<TestName> 형식으로 작성한다. 아래는 예이다.
testMethod_specificCase1 testMethod_specificCase2 void testIsDistinguishable_protanopia() { ColorMatcher colorMatcher = new ColorMatcher(PROTANOPIA) assertFalse(colorMatcher.isDistinguishable(Color.RED, Color.BLACK)) assertTrue(colorMatcher.isDistinguishable(Color.X, Color.Y)) }
'소프트웨어 > 안드로이드앱' 카테고리의 다른 글
[안드로이드 기초#1] 플랫폼과 개발환경 (1) | 2015.06.29 |
---|---|
안드로이드 스튜디오의 유용한 단축키 (3) | 2015.06.28 |
안드로이드 스튜디오 샘플코드 임포트 (0) | 2015.06.27 |
안드로이드 스튜디오 코드 포메터 (스타일/규약/convention) (1) | 2015.06.23 |
안드로이드 스튜디오에서 git 저장소 사용하기 (1) | 2015.06.22 |