'reflection'에 해당되는 글 1건

  1. 2009.06.27 Reflection을 이용해서, Java Class의 Method 목록 가져오기
오늘은 최근 며칠전에 보아 두었던, Twitter4J 의 API와 이를 Java에서 사용가능하도록 구현한 Twitter4J 를 분석하기로 마음 먹었다. 일단 웹에서 Twitte4J 2.0.8버전을 다운로드 받아서, Eclipse에서 환경을 만들어보았다. 다운로드 받은 파일을 압축을 풀면, Source Code와 Sample Code 그리고 Junit 코드가 있다.
Junit 코드는 Twitter4J 라이브러리의 사용법을 손쉽게 익힐수 있는 좋은 예제이다.

몇가지 테스트를 하다보니, 클라스의 메소드들의 내용을 출력해서 볼 필요가 있어서, 이를 Console에 찍어보았는데, 메소드 또는 getter를 통해서 Twitter 서버의 결과들을 가져와 그 내용을 살펴 볼수 있었다.

아래과 같이 Java Doc에 있는 문서를 뒤져서, System.out.println() 메소드를 이용하여 처음에 만들어 보았다.  
public void testSearch() throws TwitterException
 {
        Twitter tw = new Twitter();
        String queryStr = "happyzoo";
        Query query = new Query(queryStr);
        QueryResult queryResult = tw.search(query);
     
        List<Tweet> tweets = queryResult.getTweets();
        System.out.println("tweets.size: ==> " + tweets.size());
               
        for (Tweet tweet : tweets) {
         System.out.println("tweet.getText: ==> " + tweet.getText());
       System.out.println("tweet.getSource: ==> " + tweet.getSource());
                    ....    
        }
 }



위의 코드는 Junit을 이용하여 테스트용으로 만든 코드이지만, 코드의 내용을 검증하지는 않았다. 개인적으로는 TDD를 선호하지만, 갑작스럽게 확인하거나 할때는 그냥 junit에다 테스트 코드를 작성하고, 나중에 Refactoring을 한다. 새로운 Library를 익혀야 하거나 테스트 코드를 짜볼 때는 Junit로 테스트를 작성하고, 나주에 필요할 때, 다시 이를 보고, 사용법을 익히는 시간을 줄이곤 한다.

그리고, 사실 다음 단계들이 Refacoring을 하는 단계라고 생각할 수도 있다.
위의 코드가 그리 보기 않좋았다. 그래서 이를 다시 수정하였다. 나중에 코드를 작성할때 필요한 내용들을 확인할 때 재 사용이 가능할 거라고 생각하였다. (이 역시 나의 습관인데, 별도의 클래스의 작성 필요 없는 경우는 static으로 메소드를 선언하여 사용하곤 한다.)


private static void showTweet(Tweet tweet){
    StringBuffer showData = new StringBuffer();
    
    showData.append("tweet.getCreatedAt():" + tweet.getCreatedAt() + "\n");
    showData.append("tweet.getFromUser():" + tweet.getFromUser() + "\n");
    showData.append("tweet.getFromUserId():" + tweet.getFromUserId() + "\n");
    showData.append("tweet.getId():" + tweet.getId() + "\n");
    showData.append("tweet.getIsoLanguageCode():" + tweet.getIsoLanguageCode() + "\n");
    showData.append("tweet.getProfileImageUrl():" + tweet.getProfileImageUrl() + "\n");
    showData.append("tweet.getSource():" + tweet.getSource() + "\n");
    showData.append("tweet.getText():" + tweet.getText() + "\n");
    showData.append("tweet.getToUser():" + tweet.getToUser() + "\n");
    showData.append("tweet.getToUserId():" + tweet.getToUserId() + "\n");

  
    System.out.println("showData: " + showData);
 }

위 처럼 코드를 만들고 나서는, 다른 클래스에 대한 값들을 또한 확인해야 할 필요가 느껴졌다.
그런데, 위와 같은 "showData.append("... ")" 라고 일일이 쳐 주어야 하는 것이 비 생산적이라는 생각이 들어서, 자동으로 위와 같은 형태로 생성해 주면, 이를 출련한 후에 실제 코드에서 생성해서 붙여 쓰는 것이 더 생산적일 거라는 생각이 들어서, 코드를 만들어 주는 코드를 아래 처럼 짜써 넣었다.

public void createTempletCode()
 {
String[] methods = new String[]{
"getCreatedAt",
"getFromUser",
"getFromUserId",
"getId",
"getIsoLanguageCode",
"getProfileImageUrl",
"getSource",
"getText",
"getToUser",
"getToUserId"
  };
  
String str = "";
for (String method : methods) {
str += "showData.append(\"tweet." + buffer + "():\" + " + "tweet." + method + "() + \"\\n\");\n";
}
  
System.out.println(str);
 }

위의 코드는 실제 코드를 만들때의 노가다를 상단부분 없애 주었다. 물로 1~2를 위해서 쓴다면, 여기까지 만든 노력이 별로 효과가 없지만, 말이다. 하지만, 역시 단점이 있다, 메소드들을 java Doc을 뒤져서 복사해서 배열로 만들어 줘야 한다. 이 역시 적지 않은 노가다가 든다. Copy & Paste를 한다해도 메소드가 많다면, 이 역시 쉬운 작업을 아니다.

역시 한번더 개선을 해야 겠다. 일단 위의 코드를 아래처럼 별도이 메소드로 만들어 주었다.

 public static String createTempletCode(String[] methods, String instanceName)
 {
String temp = "";
for (String method : methods) {
   temp += "showData.append(\"" + instanceName + "." + method + "():\" + " + "tweet." + method + "() + \"\\n\");\n";
  }
   
  return temp ;
 }

그리고, reflection을 이용하기로 했다. 이는 junit과 AspetJ 또는 AOP에서 많이 사용되고 있고, 실제로 윈도우나 Java에서 최근들어 많이 사용되고 있는 방법이다. 그러나 사실 일반적인 개발시는 많이 이용하지 않고 있다. 그러나 나는 오는 나의 노가다를 쫑 내기 위해서, 이를 이용할 수 밖에 없었다.

"java.lang.reflect.Method"를 이용해서 내가 java Doc에서 가져오던 내용들을 한번에 가져오기로 하고 다음과 같이 수정하였다. 만약 제대로 가져오면, 위에 있는 메서드를 이용하여 쉽게 원하는 것을 출력할 수 있을 것이다.
 
public void createTempletCode()
 {
System.out.println(createTempletCode(getMethodOfClass(Tweet.class), "tweet")); 
 }

public static String createTempletCode(String[] methods, String instanceName)
 {
String temp = "";
for (String method : methods) {
 temp += "showData.append(\"" + instanceName + "." + method + "():\" + " + "tweet." + method + "() + \"\\n\");\n";
  }
   
  return temp ;
 }

public static String[] getMethodOfClass(Class clazz)
 {
  Class thisClass = clazz;
  Method[] methods = thisClass.getDeclaredMethods();
  
  String[] methodNames = new String[methods.length];
  
  for(int i =0; i < methods.length; i++ )
  {
methodNames[i] = methods[i].getName();
   
  }
  
  return methodNames;

}


코드가 많이 단순해 졌다. 처음 생각했던 내용들이 시간의 흐름사이에서, 생각도 변화하고 아이디어도 변화해서, 위와 같은 모습이 되었다.
조금 전까지도, 어차피 reflection을 적용했으니까, 생성한 코드를 바로 실행하는 로직을 만들어 넣을까도 생각했지만, 이거는 오버스펙이 될거라는 생각에 오늘은 더 이상 추가하지 않을 계획이다. 아직도 몇가지 코드들은 refectoring을 기다리고 있으니 말이다.

Twitter4J의 클래스와 메소드들은 생각보다 사용하기 쉽고, 단순하다.그리고, 이는 일본사람이 만들어서 인지 Unicode에 대한 문제도 없어 보인다.

오늘은 별다른 래퍼 클래스를 만들지 않고, 몇가지 테스트만 더하고, 내가 원하는 것을 한번 만들어 볼 생각이다.


Posted by 행복상자