josephjang

Refactoring: Improving the Design of Existing Code

Refactoring: Improving the Design of Existing CodeRefactoring: Improving the Design of Existing Code, by Martin Fowler

리팩토링이 없다면, 소프트웨어의 생명 주기가 진행될 수록 소프트웨어의 질은 점점 떨어질 수 밖에 없다. 현대적인 관점에서 소프트웨어 개발은 더이상 Rocket Science처럼 고정된 결과물을 산출하는 활동이 아니다. 뛰어난 소프트웨어 아키텍트라 하더라도 소프트웨어 개발 프로젝트의 초기에 그 소프트웨어를 바라보는 방식 (또는 디자인)이 후반에까지 변함없이 지속될 확률은 매우 낮다. 하물며 여러 사람이 함께 일하는 팀 소프트웨어 개발에서야 더이상 말할 것도 없다. 특히, 사용자의 요구사항이 다 떨어져서 더이상 변경할 것이 없어지지 않는 한, 소프트웨어 질의 하락은 점점 빨라질 뿐이다.

이 책은 리팩토링에 관한 바이블이다. Martin Fowler는 더이상 언급할 필요도 없는 유명한 저자다. 이 책은 리팩토링과정 예시를 통한 리팩토링에 관한 소개, 리팩토링의 정의와 중요성, 리팩토링을 언제, 어떻게 적용할 것인가에 관한 가이드, 리팩토링 패턴 카탈로그로 이루어져 있고, 리팩토링의 간략한 역사와 리팩토링의 도입 방법, 리팩토링도구에 관한 의미있는 에세이들으로 끝맺고 있다.

Martin Fowler는 리팩토링을 다음과 같이 정의하고 있다.

Refactoring: a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.

리팩토링의 목적은 소프트웨어를 좀 더 이해하게 쉽게 만들거나 그 과정에서 소프트웨어를 좀 더 이해함으로써 소프트웨어를 수정하는 비용을 줄이는 것이다. 반대로 말하면, 소프트웨어를 수정할 필요가 없다면, 리팩토링을 할 필요는 없다는 얘기가 된다. 또한 여기에는 한가지 가정이 있는데, 소프트웨어를 좀 더 이해하기 쉽다면, 수정하기도 쉽다는 것이다. 당연한 얘기같지만, 많은 개발자들은 어떤 이유에서건 이를 중요하게 생각하지 않는다.

리팩토링의 정의는 리팩토링의 가장 중요한 측면 또한 언급하고 있는데, 바로 겉으로 드러나는 소프트웨어의 동작 방식을 변경하지 않는다는 것이다. 리팩토링 패턴 카탈로그는 바로, 소프트웨어의 동작 방식을 변경하지 않는다는 제약을 위해서 어떤 방식으로 Refactoring을 수행해야하는가에 관한 패턴들을 모아둔 것이라고 볼 수 있다. 그리고, 이러한 제약을 보장하기 위해서 Unit Testing을 제안하고 있는 것이다.

패턴 카탈로그를 읽는 것은 (항상 그렇듯이) 매우 지루한 일이었다. 패턴 카탈로그는 패턴이 갖는 이점들을 제공하지만, 카탈로그를 처음부터 끝까지 읽어볼 정도로 가치가 있지는 않다. 특히, Mechanics 부분은 읽지 않아도 무방하다. 어느 정도 숙련된 개발자라면 리팩토링의 개념만 잘 알고 있다면, Mechanics를 직접 읽어볼 필요 없이 같은 것을 수행할 수 있을 것이다. 특히, 많은 수의 리팩토링 패턴들이 도구를 통해 자동화가 되어있는 현재 시점에서는 더더욱 그러하다. 내가 추천하는 방법은, 패턴의 이름(Name), 맥락(Context) 부분과 동기(Motivation) 부분만 읽고, 이해가 가지 않는 경우에만 예시(Example) 부분을 읽어보는 것이다. 내게는 패턴들을 통해 어휘를 확장시킬 수 있었던 것이 가장 큰 도움이 되었다. 예를 들어, 실제로 리팩토링을 수행할 때, 커밋 로그(Commit Log)에 리팩토링 패턴을 적어넣을 수 있어서 편리했다. 사실, 만약 이 책을 가장 효과적으로 읽고 싶다면, 패턴 카탈로그만 빼고 다 읽고 나서, 패턴 카탈로그들은 이름만 익숙해질 정도로만 훑어보라고 조언해주고 싶다.

패턴 카탈로그 읽는 것을 마치고 수십 페이지 남겨놓은 상태에서는, 마치 이미 이 책을 다 읽었다는 듯 생각을 하고 있었는데, 이 책의 마지막 장들에 들어있는 에세이들은 상당히 중요한 문제 제기와 인식을 제공해주었다.

사실상 리팩토링이라는 단어를 처음으로 만들고 초기부터 연구를 수행한 William Opdyke의 에세이는, 왜 실제 세계에서 프로그래머들은 리팩토링을 하지 않으려하는가를 따져보고, 어떻게 하면 그런 문제를 넘어 리팩토링을 적용할 수 있을 것인가에 관해서 얘기하고 있다. 자세히 설명하기 보다는 다음 문단을 인용하도록 하자.

Within Lucent/Bell Labs I found that encouraging application of reuse and platforms required reaching a variety of stakeholders. It required formulating strategy with executives, organizing leadership team meetings among middle managers, consulting with development projects, and publicizing the benefits of these technologies to broad research and development audiences through seminars and publications. Throughout it was important to train staff in the principles, address near-term benefits, provide ways to reduce overhead, and address how these techniques could be introduced safely. I had gained these insights from my refactoring research.

이 내용은 비단 리팩토링에만 해당되는 것은 아니다. 주로 단기적인 성과를 중요시하는 기업 환경에서, 장기적인 투자를 필요로 하는 무언가를 추구하기 위해서는 위에서 언급한 모든 일들을 할 수 있을 정도로 부단히 노력해야한다는 생각이 든다.

두번째 에세이는 리팩토링 도구에 관한 얘기를 하고 있는데, 이 책이 쓰여진 1999년에는 어느 정도 널리 퍼진 리팩토링 도구가 없었겠지만, 2007년 현재에는 주요 언어인 Java의 주요 IDE들이 리팩토링을 직접 지원하고 있기 때문에, 기술의 변화를 지면을 통해 느낄 수 있게 해준다.

이 책의 마무리에 해당하는 Kent Beck의 에세이는 리팩토링을 할 때 가져야할 마음가짐에 관해서 얘기하고 있는데, 중요한 항목들은 여기에 인용할 가치가 있을 것이다.

  • Get used to picking a goal
  • Stop when you are unsure
  • Backtrack
  • Duets

이 책을 읽기 시작하면서 바로 떠오른 생각은 패턴들로 리팩토링하는 리팩토링 패턴이 이 책에는 별로 없다는 것이다. 저자는 이 책에서 리팩토링 패턴의 가장 작은 단위들을 우선적으로 다루고 싶어했고, 그 목적은 의미가 있었던 것 같다. 그리고 이 책에서도 같은 아이디어를 여러번 언급하고 있다. 실제로 이 아이디어는 2004년에 출판된 Refactoring to Patterns라는 책으로 실현되었다. 시간이 되는대로 Refactoring to Patterns를 읽어볼 예정이다.

Refactoring: Improving the Design of Existing Code 더 읽기"

JRuby String doesn't seem to be compatible with Java String

JRuby String 즉, JRuby 상에서의 Ruby String과 Java String이 서로 호환되지 않는 문제를 발견했다. ASCII의 경우에는 별 문제가 없지만, 한글의 경우에는 깨지는 문제가 발생한다. JRuby 사용자가 별로 없어서 이 문제가 내 환경에 국한된 문제인지 실제로 JRuby의 문제인지 확인은 할 수 없다. JRuby 코드를 약간 들여다보아야 할 것 같다.

이 문제를 테스트하기 위해서는 다음의 Ruby 코드를 사용하면 된다. Java String은 System.out.println으로 출력했을 때만, Ruby String은 puts로 출력했을 때만 정상적으로 출력된다. 서로 호환이 된다면, 모두 정상적으로 출력되어야할 것이다.

require ‘java’

include_class ‘Foo’
include_class ‘java.lang.System’

java_string = Foo::getString # yields ‘meme메롱’ in java.lang.String form
puts java_string
System.out.println java_string

ruby_string = ‘meme메롱’
puts ruby_string
System.out.println ruby_string

다음 코드는 위의 Ruby 코드에서 사용하는 Java 클래스인 Foo 클래스.

import java.lang.String;

public class Foo {
   public static String getString() {
      return new String("meme메롱");
}
}

이 문제를 피해가기 위한 한가지 workaround는 ASCII 문자로 된 intermediate form을 두고 Java String과 Ruby String을 서로 변환하는 것이다. intermediate form은 어떤 것을 사용해도 되지만 percent encoding을 사용하여, 다음과 같은 코드를 사용할 수 있다.

require ‘java’
require ‘cgi’

include_class ‘java.net.URLEncoder’
include_class ‘java.net.URLDecoder

class StringTranslator
   def self.to_ruby_string(java_str)
      CGI.unescape(URLEncoder.encode(java_str, "UTF-8"))
end

def self.to_java_string(ruby_str)
      URLDecoder.decode(CGI.escape(ruby_str), "UTF-8")
end
end

 

JRuby String doesn't seem to be compatible with Java String 더 읽기"

Calling a Java method with variable number of arguments in JRuby

결론부터 얘기하자면, Java의 variable number of arguments(이하 varargs)를 가진 Java 메서드를 JRuby 코드에서 호출하려면, 여러 argument들을 나열하거나 Ruby Array를 사용하면 안되고, Java array 객체를 생성해주어야한다. Java의 varargs는 Java array와 동일하게 취급되므로 당연히 Java array의 경우에도 마찬가지다. 그렇다면, Ruby Array는 무엇이랑 대응될까? 바로 java.util.List다.

다음은 이러한 동작을 테스트하기 위한 간단한 Java 클래스. 위에서 언급한대로, Java array parameter를 varargs로 보면 되겠다.

import java.lang.String;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

public class Song {

    public enum Category {
       
POP, ROCK, CLASSIC, JAZZ
   
}

    private String title;
   
private List<Category> categories = new ArrayList<Category>();

    public Song(String title) {
       
this.title = title;
   
}

    public void setCategory(Category category) {
       
this.categories.clear();
       
this.categories.add(category);
   
}

    public void setCategoryList(List<Category> categories) {
       
this.categories.clear();
       
this.categories.addAll(categories);
   
}

    public void setCategories(Category[] categories) {
       
this.categories.clear();
       
this.categories.addAll(Arrays.asList(categories));
   
}
   
   
public String toString() {
       
return "Title: " + title + " Categories: " + categories;
   
}
       
}  

다음은 위의 Java 클래스를 사용하는 JRuby 코드.

require 'java'

include_class 'Song'

song = Song.new 'Waiting On The World To Change'
puts song

song.setCategory Song::Category::POP
puts song

categories = Array.new
categories.push Song::Category::POP
categories.push Song::Category::ROCK
puts categories.length
puts categories.size
song.setCategoryList categories
puts song

categories = Song::Category[].new 2
#categories.add Song::Category::POP # not working
categories[0] = Song::Category::POP
categories[1] = Song::Category::CLASSIC
puts categories.length
#puts categories.size # not working
song.setCategories categories
puts song

Calling a Java method with variable number of arguments in JRuby 더 읽기"

Support for epoll in JDK 5.0 Update 10

좀 뒤늦은 소식이지만, Sun의 JDK 5.0 Update 10에 epoll 지원이 들어갔습니다. JDK 6.0에 epoll 지원이 들어가면서 JDK 5.0에도 반영된 것 같습니다. nio가 생길 때 당연히 epoll을 지원하겠거니 생각했는데, 의외로 대단히 늦게 지원하기 시작했군요. 그럼 이전에는 select()나 poll()로 구현되어있었다는 얘기군요. epoll을 사용했을 때의 performance, scalability상의 이점은 Blackdown JVM 1.4.2를 이용한 벤치마크에서도 알 수 있습니다. Sun JDK 6.0의 nio 벤치마크 자료는 모르겠지만, 이희승님의 벤치마크에 의하면 10% 정도 향상이 있었다고 합니다. 어쨌든 희소식입니다. C/C++의 성능 우위를 뒤따라잡기 위해서 Java도 열심히 노력하고 있군요.

Support for epoll

The Linux downloads of this update release include an implementation of java.nio.channels.spi.SelectorProvider that is based on the epoll I/O event notification facility. The epoll facility is available in the Linux 2.6 kernel, and is more scalable than the traditional poll system call. This epoll-based implementation may improve the performance of server applications that use the New I/O API and that register hundreds of channels with a selector. For more information, refer to the epoll(4) and poll(2) man pages.

The epoll-based implementation of SelectorProvider is not selected by default. To select it, specify a property value from the command line as follows:

java -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider ...

(Excerpted from JDK 5.0 Release Notes)

Support for epoll in JDK 5.0 Update 10 더 읽기"

Lizardlings are Stupid

Neverwinter Nights 2의 주인공은 West Habor에 살고 있는 촉망받는 젊은이인데, 어느날 갑자기 알 수 없는 이유로 West Habor가 습격당하는 것으로 Act I은 시작됩니다. 습격자들을 물리치는 과정에서 주인공은 그들이 누군가에 의해 조종받고 있으며, 어떤 물건을 찾고 있다는 것을 알게됩니다. 주인공의 양아버지는 그 물건이 바로 자기가 예전에 숨겨둔 Silver Shard 때문일 것이라며 마을을 보호하기 위해서는 그 물건을 Neverwinter 시로 가져가야한다고 합니다. 주인공은 아버지의 명에 따라 Silver Shard를 찾으러 가는데, 그곳은 많은 수의 Lizardlings이 차지하고 있었습니다.

치르고 있던 의식을 방해받은 Lizardlings는 주인공 파티에게 적대적인 태도를 취합니다.

주인공은 여기에 대한 변명을 해야하는데, 제가 만든 주인공의 Charisma attribute가 높아서 그런지 몰라도 Diplomacy와 Bluff가 선택지로 뜹니다.

    1. [Diplomacy] 이 곳이 우리 차지였을 때, 우리 부족이 뭔가를 여기에 두고 갔소.
    2. [Bluff] 석신이 당신들의 기도를 듣고 나를 보낸거요.
    3. 문을 열어 준 것 고맙지만, 이제 죽어줘야겠어.
    4. 막 갈려던 참이었다구요.

일단 1번 답을 선택해봅시다.

성공입니다. 그런데, 자기네 땅에 들어온 건 이해하겠는데, 의식을 방해한 건 이해못하겠답니다. 교주(?)라서 그런지 몰라도 대단히 똑똑한 Lizardling이군요. 여기에 대한 답변으로 4번 선택지에 Lie가 뜨네요.

[Lie] 당신들이 여기에 오기 전부터 우리는 석신을 섬겨왔소. 석신께서는 우리들이 서로 돕기를 원할거요.

주인공의 Alignment가 Chaotic Good니까 Lie를 선택하는 것이 합당해보입니다.

역시 성공했습니다.

그렇다면, 애초에 Bluff를 선택했다면 어떨까요? 다음과 같습니다.

난 석신의 화신이다. 너희들은 나를 두려워하라!

라고 하니까 그대로 넘어가네요.

오늘의 교훈: Lizardling들은 멍청하다.

아래는 Silver Shard를 상자에서 꺼내고 있는 주인공 Elenella Lane (제가 만든 캐릭터)의 모습입니다.

Lizardlings are Stupid 더 읽기"

모든 것은 마이크로소프트 탓

이런 ‘시장 혼란’은 결국 윈도의 독점력에 따른 결과다. 다른 대안이 없는 만큼 윈도 비스타에 맞게 서비스 프로그램을 고쳐야 하는 것이다. 정통부도 ‘꼼꼼히 확인해 구매하라’는 이용자 안내에 그칠 뿐, 뽀족한 대책을 내놓지 못하고 있다.

비스타 출시에 따른 이런 혼란은 한국이 유독 심한 것으로 알려졌다. 액티브-엑스의 사용이 보편화되지 않았고 인터넷 연결이 우리나라만큼 원활하지 않기 때문이다. 익명을 요청한 정보통신부 관계자는 “지난해 3분기부터 엠에스 쪽과 공동작업을 했는데, 우리나라의 경우 응용프로그램이 발달해 손볼 대목이 많았음에도 엠에스가 기술자료 공개를 늦게 하는 바람에 이런 일이 벌어졌다”고 말했다.윈도 비스타 혼란…지금 깔면 손해, 한겨레

그렇습니다. 외국은 인터넷 연결과 같은 인프라도 미비할 뿐만 아니라, 소프트웨어가 별로 발달하지 않아서 ActiveX라는 뛰어난 기술도 아직 보편화되지 않았답니다. 게다가 마이크로소프트는 인터넷과 소프트웨어가 우월한 대한민국의 상황을 따라오지 못하고 늑장대처를 하는 후진국 기업의 행태를 보여주고 있습니다.

모든 것은 마이크로소프트 탓 더 읽기"

Next Release of IBM Lotus Notes and Domino

IBM also said the long-awaited next release of IBM Lotus Notes and Domino, known until now as “Hannover,” will enter the public beta phase of its testing in February and should be generally available mid-year. (From IBM Expands Line of Collaboration Software Tools)

IBM의 그룹웨어 솔루션인 Lotus Notes와 Domino의 차기버전 Hannover가 나온단다. Lotus Notes의 Usability는 그야말로 최악이다. (Lotus Notes Sucks) 그냥 출시안하면 안되겠니?

Next Release of IBM Lotus Notes and Domino 더 읽기"

Detecting Duplicated Code in Eclipse

중복된 코드(Duplciated Code)는 Long Method와 함께 Bad Smell의 대표적인 사례 중 하나다. 사람이 중복된 코드를 찾기 위해서는 모든 코드를 봐야하지만, 컴퓨터는 이를 손쉽게 할 수 있다. 컴퓨터가 쉽게 할 수 있는 일은 컴퓨터에게 맡기자.

Eclipse에서 중복된 코드를 탐지해주는 플러그인은 두가지가 있다. 예전에 한번 언급했던 PMD의 부가적인 기능인 CPD(Copy/Paste Detector)를 사용하거나 SDD (프로젝트 사이트)를 사용할 수 있다.

두 플러그인 모두 중복된 코드를 탐지하는 기능 자체는 잘 동작하나, 완성도가 좀 부족해보인다. CPD의 경우에는 중복된 코드에 대한 리포트를 텍스트 파일로 출력해주고, SDD는 Eclipse의 View를 사용해 바로바로 중복된 코드로 찾아갈 수 있도록 되어있으나, 라인 단위가 아닌 문자 단위라서 조금 불편하다. 두 플러그인 모두 하나의 프로젝트 내에서만 중복 코드 탐지가 가능하기 때문에, 여러 프로젝트 간에 코드를 Copy-and-Paste하는 경우를 탐지할 수 없다.

재미있게도 SDD는 정일영이라는 고려대 컴퓨터교육과 분이 만드셨는데, 이걸로 OOPSLA 2005 포스터 세션에 나가신 모양이다. 왠지 얼굴을 알고 있는 분이라는 생각이…

덧붙여, 코드 분석에 관련된 Eclipse 플러그인에 관심이 많다면 Automation for the people: Improving code with Eclipse plugins 이라는 기사가 도움이 될 것이다. Test coverage를 위한 Coverclipse나 Dependency analysis를 위한 JDepend와 같은 플러그인들은 아직 시도해보지 못했다.

Detecting Duplicated Code in Eclipse 더 읽기"