2013. 8. 31. 00:18

회사에서 개발 중 코드 쪽 부분이 지저분하여 리팩토링을 진행 하였다.


Code클래스를 상속 받은 하위 클래스들이 17개 정도 되는데, 

하위 클래스들을 하나하나 대응 해주니 중복되는 부분이 많아져서 코드량이 쓸 때 없이 많았다.


그래서 상위 클래스인 Code클래스로 처리를 해주었더니, 코드량이 1/10로 줄였다.


유닛테스트로 테스트를 실행 했더니 초록막대! 성공!!!


하지만 기쁨도 잠시.. 실제로 돌려보니.. 바로 문제가 발생했다.


Code클래스가 @Entity로 잡혀 있는데, 이 Code클래스를 persist 할 때, 

DB Code table에 DTYPE이라는 컬럼에 'Code' 라고 저장 되어졌다.


여기서 문제점은 DTYPE이라는 컬럼에 'Code'란 값만 들어가면 안된다는 것이다.


Code클래스의 하위클래스명까지 같이 들어가야하는 문제가 있다.


예를 들어, Code클래스를 상속받은 Category클래스가 있고, 

이 Category를 persist 하면 DTYPE 컬럼에 'Code$Category'라고 값이 삽입되어야한다.


물론, 이부분은 내가 해주는게 아니라.. Spring data jpa에서 해주는 것 같다.


그래서 이 문제를 해결 하기 위해서, Reflection을 이용하기로 했다.


Reflection을 이용해서 클래스 생성자를 호출 하면 이 문제는 쉽게 해결 되는 것 이다.

이제 코드를 보겠다.


일단 샘플로 도메인 클래스들을 보겠다.



public class Code {
    private String code;
    private CodeType type;

    Code(String code, CodeType type) {
        this.code = code;
        this.type = type;
    }

    String getCode() {
        return code;
    }


    CodeType getType() {
        return type;
    }
}

public class Category extends Code{
    public Category(String code) {
        super(code, CodeType.CATERGORY);
    }
}

public class Division extends Code{

    public Division(String code) {
        super(code, CodeType.DIVISION);
    }
}

public class Section extends Code{
    public Section(String code) {
        super(code, CodeType.SECTION);
    }
}

enum CodeType {
    CATERGORY(Category.class),
    DIVISION(Division.class),
    SECTION(Section.class)
    ;

    private Class clazzType;

    CodeType(Class clazzType) {
        this.clazzType = clazzType;
    }

    Class getClazzType() {
        return clazzType;
    }
}

public class CodeForm {
    String code;
    CodeType type;

    CodeForm(String code, CodeType type) {
        this.code = code;
        this.type = type;
    }

    String getCode() {
        return code;
    }

    CodeType getType() {
        return type;
    }
}


이제 테스트 코드를 보겠다.


public class ReflectionTest {

    private Code category;
    private Code division;
    private Code section;

    @Before
    public void setUp() throws Exception {
        category = codeConstructReflection(new CodeForm("category", CodeType.CATERGORY));
        division = codeConstructReflection(new CodeForm("category", CodeType.DIVISION));
        section = codeConstructReflection(new CodeForm("category", CodeType.SECTION));
    }

    @Test
    public void reflectionSubCodes() {
        assertTrue(Category.class.isAssignableFrom(category.getClass()));
        assertTrue(Division.class.isAssignableFrom(division.getClass()));
        assertTrue(Section.class.isAssignableFrom(section.getClass()));

    }

    private Code codeConstructReflection(CodeForm form) {
        try {

            Class[] paramTypes = {String.class};
            Class reflectionCode = form.getType().getClazzType();
            Constructor constructor = reflectionCode.getConstructor(paramTypes);
            return (Code) constructor.newInstance(new Object[]{form.getCode()});

        } catch (NoSuchMethodException e) {

        } catch (InvocationTargetException e) {

        } catch (InstantiationException e) {

        } catch (IllegalAccessException e) {

        }
        return null;
    }
}


아래에 리플렉션을 이용해서 클래스를 생성하는 모습에 대해서 설명 해주겠다.


Class[] paramTypes = {String.class}; //생성자에 삽입될 파라미터 타입을 지정해준다. 복수가 될수 있다. {String.class, Integer.class....}


Class reflectionCode = form.getType().getClazzType(); // CodeType에 있는 clazzType을 Class로 만든다.


Constructor constructor = reflectionCode.getConstructor(paramTypes); // CodeType.clazzType 을 이용하여 Constructor클래스를 생성한다.


(Code) constructor.newInstance(new Object[]{form.getCode()});  // newInstance(배열값)에 paramTypes에 정의된 순서대로 파라미터를 넣어주면 된다.






이상 글을 마치겠습니다.











Posted by dragonSilver