[이미지 출처 - eHow.com] |
- 상수이해하기
- 안전한 상수
- Immutable 객체
- 읽기전용 인터페이스
- 복제하기(cloning)
- 복제가능한 읽기전용 객체
Review
마지막으로 작성한 AlienRace 클래스를 다시 살펴봅시다.
public final class AlienRace { //default constant public static final AlienRace BORG = null; public static final AlienRace FERENGI = new AlienRace(); private AlienRace() {} }AlienRace 클래스를 사용하는 클라이언트 입장에서는 다음과 같은 코드도 런타임 오류의 가능성 없이 사용할 수 있습니다.
public static void constTest(AlienRace alien) { if(alien == AlienRace.BORG) //... if(alien == AlienRace.FERENGI) //... } public static void run() { //case#1. constTest(AlienRace.BORG); //case#2. constTest(AlienRace.FERENGI); //case#3: OK!! use default value BORG!! constTest(null); }
문제는 AlienRace 클래스의 각 객체가 상태를 가질때입니다. 예를 들어, 각 상수마다 이름(Name) 속성을 가져야 할 경우, AlienRace 클래스에 String 타입의 name 필드와 getName()과 같은 selector를 추가할 수도 있겠지만, 기본적으로 null로 할당된 BORG는 생성자에서 이름 필드를 초기화 할 수도 없을 뿐더러, getName() 메서드도 호출할 수 없는 구조를 가지고 있습니다. 이러한 경우, BORG의 속성값을 대신 보관하고 있다가 속성값 요청시 대신 반환해 주는 대리자(proxy)를 이용하는 방법으로 문제를 해결할 수 있습니다.
중간단계에서 대리역할을 하는 proxy 클래스는 다음과 같습니다.
class ConstantProxy { private AlienRace mAlien; private String mName; public ConstantProxy(AlienRace alien, String name) { mAlien = alien; mName = name; } public boolean isHolding(AlienRace alien) { return (mAlien == alien); } public String getName() { return mName; } }
그리고 AlienRace 클래스는 다음과 같이 확장하였습니다.
public final class AlienRace { public static final AlienRace BORG = null; public static final AlienRace FERENGI = new AlienRace(); private AlienRace() {} private static Vector<ConstantProxy> mProxies = new Vector<ConstantProxy>(); static { mProxies.addElement(new ConstantProxy(BORG, "Borg")); mProxies.addElement(new ConstantProxy(FERENGI, "Ferengi")); } public static String valueOf(AlienRace alien) { ConstantProxy target = null; Enumeration<ConstantProxy> e = mProxies.elements(); while(e.hasMoreElements()) { target = e.nextElement(); if(target.isHolding(alien)) break; } return target.getName(); } }
수정된 AlienRace 클래스의 특징은 다음과 같습니다.
- 속성 필드를 ConstantProxy가 보관
- 클래스 로드시점에 두 proxy 객체를 미리 생성
- valueOf() 메서드를 통해 속성 필드 접근
AlienRace 클래스를 사용하는 클라이언트 입장에서는 AlienRace.BORG, AlienRace.FERENGI, AlienRace.valueOf() 메서드를 통해 두 상수객체를 안전하게 사용할 수 있습니다. 다음 코드는 테스트 코드입니다.
public class AlienRaceTest { public static void constantTest(AlienRace alien) { System.out.println(AlienRace.valueOf(alien)); } public static void main(String[] args) { //case#1. constantTest(AlienRace.BORG); //case#2. constantTest(AlienRace.FERENGI); //case#3. use default value: BORG constantTest(null); System.exit(0); } }이전과 가장 큰 차이점은 인자로 null(기본값 BORG)을 넘겨준다 하더라도 속성 필드의 값을 얻을 수 있다는 점이고, constantTest() 메서드의 경우 AlienRace.valueOf() 메서드로 비교구문 없이 필드값을 출력할 수 있습니다.
지금까지 컴파일러의 도움을 통해 안전하게 상수를 정의하여 사용하는 방법에 대해 살펴봤습니다. Java1.5의 Enum 클래스가 지원되기 전 많이 논의된 주제이고, 한 번쯤 설계방법에 대해 고민해 보는것이 어떨까하여 포스팅을 했습니다.
상수에 대해서 얘기를 했으나, 사실 상수란 변하지 않는 속성을 가지고 있어야 진정한 상수입니다. 다음 글에서 immutable 객체에 대해 얘기해 보도록 하겠습니다.
1 댓글
좋은 글이네요, 감사합니다~
답글삭제