회사에서 개발 중 코드 쪽 부분이 지저분하여 리팩토링을 진행 하였다.
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에 정의된 순서대로 파라미터를 넣어주면 된다.
이상 글을 마치겠습니다.