검색결과 리스트
2006/리팩토링에 해당되는 글 8건
- 2006/11/17 이클립스를 활용한 메소드 시그너처 변경(Alt+Shift+C) (1)
- 2006/11/17 이클립스를 활용한 Extract Interface 리팩토링
- 2006/11/15 이클립스를 활용한 Extract Local Variable 리팩토링
- 2006/11/11 공통 인터페이스에 Generics 적용
- 2006/11/11 공통 서비스 추출
- 2006/11/11 코드를 간결하고 명확하게 하기
- 2006/11/11 Extract Method 리팩토링
- 2006/11/11 이클립스를 활용한 Pull-up 리팩토링
글
이클립스를 활용한 메소드 시그너처 변경(Alt+Shift+C)
2006/리팩토링
2006/11/17 19:28
public abstract String findByDong(String dong);
반환값이 String이었는데 여러 개의 객체 배열로 반환받고 싶다. 그리고, static 메소드로 변경하고 싶다. 이럴 때도 이클립스 리팩토링 기능을 이용할 수 있다. 편집기에서 메소드 부분(주석 포함)을 선택하거나 패키지 탐색기에서 메소드를 선택하고 오른쪽 마우스를 누른다.
Alt+Shift+C 조합키를 단축키로 사용할 수 있음을 알 수 있다.
접근 지정자, 반환값 유형, 매개변수, 예외, 메소드 이름 등 모든 것을 바꿀 수 있다. ^^; 아쉽게도 static 은 여기서 부여할 수 없다. 변경을 적용하기 전에 Preview 버튼을 선택하면 미리 보기를 통해 변경되는 부분을 확인할 수 있다.
인터페이스와 이를 구현한 클래스에 적용이 미침을 알 수 있고, 코드 비교를 통해 구체적으로 어떻게 바뀌는지 확인할 수 있다.
원문 작성 일시: 2004/11/19 (금) 13:57
글
이클립스를 활용한 Extract Interface 리팩토링
2006/리팩토링
2006/11/17 19:23
리팩토링 카다로그에 보면 Extract Interface가 있다.
Several clients use the same subset of a class's interface, or two classes have part of their interfaces in common.
동일한 클래스 인터페이스(public method)를 여러 클라이언트가 공유하는 경우, 혹은 두 개 이상의 클래스가 동일한 인터페이스를 공통으로 갖고 있는 경우 이를 인터페이스를 뽑아낸다.


이클립스에서 이러한 기능을 지원해준다. 물론, 도와주는 것이지 판단은 사람의 몫이다. ^^;
아래와 같은 코드가 있다고 하자.
public class ZipcodeInDB {
public String findByDong(String dong){
return "";
}
}
return "";
}
}
내용은 없지만, findByDong이 다른 클래스에도 있을 수 있어, Zipcode라는 인터페이스를 만들어보겠다. 패키지 탐색기에서 ZipcodeInDB를 선택하고 오른쪽 마우스를 누르면, Refactor > Extract Interface 라는 메뉴를 선택할 수 있다.
Interface name란에 원하는 인터페이스(자바 Inteface) 이름을 넣고, 인터페이스에 포함시킬 메소드를 선택한다. 가장 상당은 옵션은 현재 ZipcodeInDB라는 클래스를 참조하는 코드에서 ZipcodeInDB 대신에 Zipcode 타입으로 참조하게 하고자 할 때 선택한다.
public class ZipcodeInDB implements Zipcode
Interface name란에 원하는 인터페이스(자바 Inteface) 이름을 넣고, 인터페이스에 포함시킬 메소드를 선택한다. 가장 상당은 옵션은 현재 ZipcodeInDB라는 클래스를 참조하는 코드에서 ZipcodeInDB 대신에 Zipcode 타입으로 참조하게 하고자 할 때 선택한다.
이렇게 하면, 당연히 코드에 변경이 일어나야 한다. 확인해보자.
public class ZipcodeInDB implements Zipcode
ZipcodeInDB에는 "implments Zipcode" 부분이 추가되었고, 인터페이스 코드는 새로 생성되었다.
public interface Zipcode {
public abstract String findByDong(String dong);
}
public abstract String findByDong(String dong);
}
원문 작성 일시: 2004/11/19 (금) 13:27
글
이클립스를 활용한 Extract Local Variable 리팩토링
2006/리팩토링
2006/11/15 12:05
문자열을 블럭 지정하고 Alt+Shift+L
이클립스 리팩토링 기능을 쓰지 않고
'블럭 지정 > Ctrl+X > Ctrl+V > 타이핑'하는 것이 더 빠를 수도 있겠다.
그런데 왜 이렇게 하냐고 물으면
1) 익숙한 것과의 이별(리팩토링과의 새로운 만남을 위해선 이별이 필요하다.)
2) final을 빠뜨리지 않을 수 있는 기회를 제공1
3) 기타 의견: Alt+Shift+L을 누르고 빠르게 타이핑하는 모습이 숙달되면.. 왠지 간지가 난다. ^_____^
- '느닷없이 왠 final이냐' 하시는 분이라면 자바의 final 키워드의 용도를 읽어보삼 [본문으로]
글
공통 인터페이스에 Generics 적용
2006/리팩토링
2006/11/11 21:50
공통 서비스 추출을 수행하면 문제가 하나 발생한다. 애초에 메소드가 다음과 같았기 때문에 MemberService에서는 바로 이를 활용할 수 없다는 점이다.
한가지 방법은 타입을 없애고 Object의 List로 받는 방법이다.
이렇게 되면 엄격한 타입 체크가 불가능한 동시에 클라이언트에게 명시적 casting의 부담을 준다.
자바 5를 사용한다면 Generics를 활용해서 이를 개선할 수 있다.
/**
* 시작 Date와 종료 Date 사이에 작성된 T 객체를 얻는다.
* @param from 시작 Date
* @param to 종료 Date
* @return 조건을 만족하는 T 객체 List
*/
public List<T> findByDate(Date from, Date to);
/**
* 특정 Date를 기준으로 이전 혹은 이후의 T 객체를 얻는다.
* <p>BEFORE 이전</p>
* <p>AFTER 이후</p>
* @param beforeOrAfter
* @param timestamp
* @return 조건을 만족하는 T 객체 List
*/
public List<T> findByDate(Integer beforeOrAfter, Date timestamp);
/**
* 주어진 연을 기준으로 T 객체 반환
* @param year 연
* @return 조건을 만족하는 T 객체 List
*/
public List<T> findByDate(Integer year);
먼저, 인터페이스 정의에서 <T>로 타입을 명기한다. 그리고, 하위 인터페이스 정의를 다음과 같이 변경한다.
MemberService를 구현한 클래스는 Quick Fix를 수행하면 아래와 같이 메소드 템플릿이 적용될 것이다.
public List<Post> findByDate(Date from, Date to);
public List<Post> findByDate(Integer beforeOrAfter, Date timestamp);
public List<Post> findByDate(Integer dateCode, Integer date);
public List<Post> findByDate(Integer beforeOrAfter, Date timestamp);
public List<Post> findByDate(Integer dateCode, Integer date);
한가지 방법은 타입을 없애고 Object의 List로 받는 방법이다.
public List findByDate(Date from, Date to);
public List findByDate(Integer beforeOrAfter, Date timestamp);
public List findByDate(Integer dateCode, Integer date);
public List findByDate(Integer beforeOrAfter, Date timestamp);
public List findByDate(Integer dateCode, Integer date);
이렇게 되면 엄격한 타입 체크가 불가능한 동시에 클라이언트에게 명시적 casting의 부담을 준다.
자바 5를 사용한다면 Generics를 활용해서 이를 개선할 수 있다.
public interface FindByDateService<T> {
/**
* 특정 Date 이후를 검색
*/
public static final Integer AFTER = 5;
* 특정 Date 이후를 검색
*/
public static final Integer AFTER = 5;
/**
* 특정 Date 이전을 검색
*/
public static final Integer BEFORE = 4;
* 특정 Date 이전을 검색
*/
public static final Integer BEFORE = 4;
/**
* 시작 Date와 종료 Date 사이에 작성된 T 객체를 얻는다.
* @param from 시작 Date
* @param to 종료 Date
* @return 조건을 만족하는 T 객체 List
*/
public List<T> findByDate(Date from, Date to);
/**
* 특정 Date를 기준으로 이전 혹은 이후의 T 객체를 얻는다.
* <p>BEFORE 이전</p>
* <p>AFTER 이후</p>
* @param beforeOrAfter
* @param timestamp
* @return 조건을 만족하는 T 객체 List
*/
public List<T> findByDate(Integer beforeOrAfter, Date timestamp);
/**
* 주어진 연을 기준으로 T 객체 반환
* @param year 연
* @return 조건을 만족하는 T 객체 List
*/
public List<T> findByDate(Integer year);
/**
* 주어진 연, 월을 기준으로 T 객체 반환
* @param year 연
* @param month 월
* @return 조건을 만족하는 T 객체 List
*/
public List<T> findByDate(Integer year, Integer month);
* 주어진 연, 월을 기준으로 T 객체 반환
* @param year 연
* @param month 월
* @return 조건을 만족하는 T 객체 List
*/
public List<T> findByDate(Integer year, Integer month);
/**
* 주어진 연, 월, 일을 기준으로 T 객체 반환
* @param year 연
* @param month 월
* @param day 일
* @return 조건을 만족하는 T 객체 List
*/
public List<T> findByDate(Integer year, Integer month, Integer day);
* 주어진 연, 월, 일을 기준으로 T 객체 반환
* @param year 연
* @param month 월
* @param day 일
* @return 조건을 만족하는 T 객체 List
*/
public List<T> findByDate(Integer year, Integer month, Integer day);
}
먼저, 인터페이스 정의에서 <T>로 타입을 명기한다. 그리고, 하위 인터페이스 정의를 다음과 같이 변경한다.
public interface MemberService extends FindByDateService<Member>
public interface ForumService extends FindByDateService<Post>
MemberService를 구현한 클래스는 Quick Fix를 수행하면 아래와 같이 메소드 템플릿이 적용될 것이다.
public List<Member> findByDate(Date from, Date to) {
// TODO Auto-generated method stub
return null;
}
// TODO Auto-generated method stub
return null;
}
public List<Member> findByDate(Integer beforeOrAfter, Date timestamp) {
// TODO Auto-generated method stub
return null;
}
// TODO Auto-generated method stub
return null;
}
public List<Member> findByDate(Integer year) {
// TODO Auto-generated method stub
return null;
}
// TODO Auto-generated method stub
return null;
}
public List<Member> findByDate(Integer year, Integer month) {
// TODO Auto-generated method stub
return null;
}
// TODO Auto-generated method stub
return null;
}
public List<Member> findByDate(Integer year, Integer month, Integer day) {
// TODO Auto-generated method stub
return null;
}
// TODO Auto-generated method stub
return null;
}
글
테스트 메소드를 작성하다 보니 중복이 발생한다. Post 객체 검색을 위한 테스트 코드의 일부가...
// 2. 범위 검색: created
// 2.1 연
Integer year = 2006;
posts = forumService.findByDate(ForumService.YEAR, year);
// 2.1 연
Integer year = 2006;
posts = forumService.findByDate(ForumService.YEAR, year);
for(Post post : posts){
Calendar date = new GregorianCalendar();
date.setTime(post.getCreated());
Calendar date = new GregorianCalendar();
date.setTime(post.getCreated());
assertEquals(year.intValue(), date.get(Calendar.YEAR));
}
}
// 2.2 월
Integer month = Calendar.AUGUST;
posts = forumService.findByDate(ForumService.MONTH, month);
Integer month = Calendar.AUGUST;
posts = forumService.findByDate(ForumService.MONTH, month);
for(Post post : posts){
Calendar date = new GregorianCalendar();
date.setTime(post.getCreated());
Calendar date = new GregorianCalendar();
date.setTime(post.getCreated());
assertEquals(month.intValue(), date.get(Calendar.MONTH));
}
}
// 2.3 일
Integer day = 15;
posts = forumService.findByDate(ForumService.DAY, day);
Integer day = 15;
posts = forumService.findByDate(ForumService.DAY, day);
위와 같을 때, 전혀 다른 객체인 Member의 경우도...
겹치는 부분 즉, 중복의 냄새가 초장에 난다.
겹쳐지는 부분을 상위 인터페이스로 정의한다.
이클립스를 활용하는 경우 인터페이스를 새로 만들고 나서 ForumService 인터페이스가 이를 상속하게 하고 나서
드래그앤드롭으로 상수(contants)와 메소드를 상위 인터페이스로 이동시키면 된다.
이젠 MemberService는 추가적인 상수나 메소드 정의 없이 상속을 이용하면 된다.
글
코드를 간결하고 명확하게 하기
2006/리팩토링
2006/11/11 21:30
재방송(원문 작성 일시:2005/06/07 (화) 10:18)
Code cleaning : First, I run a quick and fully automated analysis of the code, with just a few rules:
Code clarifying : I examine the code structure, starting with the 2-mile high view of the project, and going down. Along the way, I would perform :
리팩토링(Refactoring)의 다른 말이 될 수도 있고, 리팩토링 방안 일부가 될 수도 있고
이걸 또 두개의 카테고리로 나눴는데 말장난 같기도 하지만
체크리스트 정도로는 쓰일 수 있을 듯
Code cleaning : First, I run a quick and fully automated analysis of the code, with just a few rules:
- remove dead code (classes, methods).
- remove unused method parameters, variables
- remove useless variable initialisation
- remove useless Casting
- tighten visibility (public => private)
- move tests code to a separate code tree
- modernize (Java1.4 => Java5)
Code clarifying : I examine the code structure, starting with the 2-mile high view of the project, and going down. Along the way, I would perform :
- rename : (package, class, method, parameter
- move : package, classes, methods, ...
- extract/inline method
- introduce variable
글
Extract Method 리팩토링
2006/리팩토링
2006/11/11 17:00
JetBrain IDEA 를 쓰는 이들이 refactor 기능에 매료된다고 하는데
이클립스 리팩토링도 많이 개선된 것 같다.
평소 refactor > rename 정도만 쓰다가
Extract Method 를 썼는데 아주 intelligent 하네. :)
메소드 내부에 아래와 같은 코드 블럭이 있었다.
// 테스트를 수행자의 작업 디렉토리 루트를 읽어옴
Properties properties = new Properties();
File file = new File("test/testng.properties");
properties.load(new FileInputStream(file));
String projectRoot = properties.getProperty("project.root");
Properties properties = new Properties();
File file = new File("test/testng.properties");
properties.load(new FileInputStream(file));
String projectRoot = properties.getProperty("project.root");
메소드 내부를 간결하게 하기 위해서 private method 로 분리하고자 할 때
글
이클립스를 활용한 Pull-up 리팩토링
2006/리팩토링
2006/11/11 00:42
Spring을 사용하다 보면 협업 객체의 IoC 기능 활용을 위해 setter가 요구된다.
public class ArticleListController extends AbstractController{
private ArticleLinkDao articleLinkDao;
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request,
...
}
public void setArticleLinkDao(ArticleLinkDao articleLinkDao) {
this.articleLinkDao = articleLinkDao;
}
}
그런데 이러한 클래스가 또 만들어야 한다고 가정해보자. 중복이 발생한다.
public class ThemePageController extends AbstractController{
private ArticleLinkDao articleLinkDao;
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request,
...
}
public void setArticleLinkDao(ArticleLinkDao articleLinkDao) {
this.articleLinkDao = articleLinkDao;
}
}
중복을 막기 위해서 BaseController를 만들어 이들을 상속하게 하자.
1. AbstractController -> BaseController로 변경
public class ArticleListController extends BaseController{
private ArticleLinkDao articleLinkDao;
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request,
...
}
public void setArticleLinkDao(ArticleLinkDao articleLinkDao) {
this.articleLinkDao = articleLinkDao;
}
}
2. 빠른 수정(Quick fix)으로 BaseController 생성
BaseController가 AbstractController를 상속해야 함을 잊지 말자.
3. Refactor > Pull up... 메뉴 선택
리팩토링을 하고 나면 기분까지 개운(?)해진다. ^^;
4. BaseController 변경
BaseController를 abstract로 만들어 반드시 handleRequestInternal을 구현하게 하고, 매개변수 이름을 request, response과 같이 보다 의미있게 바꿔주는 센스를 발휘하면, 마법사 혹은 빠른 수정으로 상속 객체를 생성할 때 이를 그대로 사용하게 된다. ^^
public abstract class BaseController extends AbstractController {
protected ArticleLinkDao articleLinkDao;
@Override
abstract protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception;
abstract protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception;
public void setArticleLinkDao(ArticleLinkDao articleLinkDao) {
this.articleLinkDao = articleLinkDao;
}
this.articleLinkDao = articleLinkDao;
}
}
BaseController를 abstract로 만들어 반드시 handleRequestInternal을 구현하게 하고, 매개변수 이름을 request, response과 같이 보다 의미있게 바꿔주는 센스를 발휘하면, 마법사 혹은 빠른 수정으로 상속 객체를 생성할 때 이를 그대로 사용하게 된다. ^^












