메소드 정리
  • Extract Method(136) - 그룹으로 함께 묶을 수 있는 코드 조각이 있으면, 코드의 목적이 잘 드러나도록 메소드의 이름을 지어 별도의 메소드로 뽑아낸다.
  • Inline Method(144) - 메소드 몸체가 메소드의 이름 만큼이나 명확할 때는, 호출하는 곳에 메소드의 몸체를 넣고, 메소드를 삭제하라.
  • Inline Temp(146) - 간단한 수식의 결과값을 가지는 임시변수가 있고, 그 임시변수가 다른 리팩토링을 하는데 방해가 된다면, 이 임시변수를 참조하는 부분을 모두 원래의 수식으로 바꾸라.
  • Replace Temp with Query(147) - 어떤 수식의 결과값을 저장하기 위해서 임시변수를 사용하고 있다면, 수식을 뽑아내서 메소드로 만들고, 임시변수를 참조하는 곳을 찾아 모두 메소드 호출로 바꾼다. 새로 만든 메소드는 다른 메소드에서도 사용될 수 있다.
  • Introduce Explaining Variable(151) - 복잡한 수식이 있는 경우에는, 수식의 결과나 또는 수식의 일부에 자신의 목적을 잘 설명하는 이름으로 된 임시변수를 사용하라.
  • Split Temporary Variable(155) - 루프 안에 있는 변수나 collecting temporary variable도 아닌 임시변수에 값을 여러 번 대입하는 경우에는, 각각의 대입에 대해서 따로따로 임시변수를 만들어라.
  • Remove Assignments to Parameters(159) - 파라미터에 값을 대입하는 코드가 있으면, 대신 임시변수를 사용하도록 하라.
  • Replace Method with Method Object(163) - 긴 메소드가 있는데, 지역변수 때문에 Extract Method를 적용할 수 없는 경우에는, 메소드를 그 자신을 위한 객체로 바꿔서 모든 지역변수가 그 객체의 필드가 되도록 한다. 이렇게 하면 메소드를 같은 객체 안의 여러 메소드로 분해할 수 있다.
  • Substitute Algorithm(167) - 알고리즘을 보다 명확한 것으로 바꾸고 싶을 때는, 메소드의 몸체를 새로운 알고리즘으로 바꾼다.

객체간의 기능 이동
  • Move Method(170) - 메소드가 자신이 정의된 클래스보다 다를 클래스의 기능을 더 많이 사용하고 있다면, 이 메소드를 가장 많이 사용하고 있는 클래스에 비슷한 몸체를 가진 새로운 메소드를 만들어라. 그리고 이전 메소드는 간단한 위임으로 바꾸거나 완전히 제거하라.
  • Move Field(175) - 필드가 자신이 정의된 클래스보다 다른 클래스에 의해서 더 많이 사용되고 있다면, 타겟 클래스(target class)에 새로운 필드를 만들고 기존 필드를 사용하고 있는 모든 부분을 변경하라.
  • Extract Class(179) - 두 개의 클래스가 해야 할 일을 하나의 클래스가 하고 있는 경우, 새로운 클래스를 만들어서 관련 있는 필드와 메소드를 예전 클래스에서 새로운 클래스로 옮겨라.
  • Inline Class(184) - 클래스가 하는 일이 많지 않은 경우에는, 그 클래스에 있는 모든 변수와 메소드를 다른 클래스로 옮기고 그 클래스를 제거하라.
  • Hide Delegate(187) - 클라이언트가 객체의 위임 클래스를 직접 호출하고 있는 경우, 서버에 메소드를 만들어서 대리객체(delegate)를 숨겨라.
  • Remove Middle Man(191) - 클래스가 간단한 위임을 너무 많이 하고 있는 경우에는, 클라이언트가 대리객체(delegate)를 직접 호출하도록 하라.
  • Introduce Foreign Method(194) - 사용하고 있는 서버 클래스에 부가적인 메소드가 필요하지만 클래스를 수정할 수 없는 경우에는, 첫 번째 인자로 서버 클래스의 인스턴스를 받는 메소드를 클라이언트에 만들어라.
  • Introduce Local Extension(196) - 사용하고 있는 서버 클래스에 여러 개의 메소드를 추가할 필요가 있지만 서버 클래스를 수정할 수 없는 경우, 필요한 추가 메소드를 포함하는 새로운 클래스를 만들어라. 이 확장 클래스를 원래 클래스의 서브클래스 또는 래퍼(wrapper) 클래스로 만들어라.

데이터 구성
  • Self Encapsulate Field(205) - 필드에 직접 접근하고 있는데 필드에 대한 결합이 이상해지면, 그 필드에 대한 get/set 메소드를 만들고 항상 이 메소드를 사용하여 필드에 접근하라.
  • Replace Data Value with Object(209) - 추가적인 데이터나 동작을 필요로 하는 데이터 아이템이 있을 때는, 데이터 아이템을 객체로 바꾸어라.
  • Change Value to Reference(213) - 동일한 인스턴스를 여러 개 가지고 있는 클래스가 있고 여러 개의 동일한 인스턴스를 하나의 객체로 바꾸고 싶으면, 그 객체를 참조 객체로 바꾸어라.
  • Change Reference to Value(217) - 작고, 불변성(immutable)이고, 관리하기가 어려운 참조 객체(reference object)가 있는 경우, 그것을 값 객체(value object)로 바꾸어라.
  • Replace Array with Object(220) - 배열의 특정 요소가 다른 뜻을 가지고 있다면, 배열을 각각의 요소에 대한 필드를 가지는 객체로 바꿔라.
  • Duplicate Observed Data(224) - GUI 컨트롤에서만 사용 가능한 도메인 데이터가 있고, 도메인 메소드에서 접근이 필요한 경우, 그 데이터를 도메인 객체로 복사하고, 옵저버(observer)를 두어 두 데이터를 동기화하라.
  • Change Unidirectional Association to Bidirectional(232) - 각각 서로의 기능을 필요로 하는 클래스가 있는데, 링크가 한쪽 방향으로만 되어 있는 경우, 반대 방향으로 포인터를 추가하고, 수정자(modifier)가 양쪽 세트(set)를 모두 업데이트 하게 변경하라.
  • Change Bidirectional Association to Unidirectional(236) - 서로 링크를 가지는 두 개의 클래스에서 한 쪽이 다른 한쪽을 더 이상 필요로 하지 않을 때는 불필요한 링크를 제거하라.
  • Replace Magic Number with Symbolic Constant(240) - 특별한 의미를 가지는 숫자 리터럴이 있으면, 상수를 만들고, 의미를 잘 나타내도록 이름을 지은 다음, 숫자를 상수로 바꾸어라.
  • Encapsulate Field(242) - public 필드가 있는 경우, 그 필드를 private으로 만들고, 접근자를 제공하라.
  • Encapsulate Collection(244) - 컬렉션(Collection)을 리턴하는 메소드가 있으면, 그 메소드가 읽기전용 뷰(read-only view)를 리턴하도록 만들고, add/remove 메소드를 제공하라.
  • Replace Record with Data Class(254) - 전통적인 프로그래밍 환경에서의 레코드 구조에 대한 인터페이스가 필요한 경우, 그 레코드를 위한 데이터 객체를 만들어라.
  • Replace Type Code with Class(255) - 클래스의 동작에 영향을 미치지 않는 숫자로 된 타입 코드(numeric type code)가 있으면, 숫자를 클래스로 바꾸어라.
  • Replace Type Code with Subclass(261) - 클래스의 동작에 영향을 미치는 변경 불가능한(immutable) 타입 코드가 있다면, 타입 코드를 서브클래스로 바꾸어라.
  • Replace Type Code with State/Strategy(265) - 클래스의 동작에 영향을 미치는 타입 코드가 있지만 서브클래싱을 할 수 없을 때는, 타입 코드를 스테이트(State) 객체로 바꾸어라.
  • Replace Subclass with Fields(270) - 상수 데이터를 리턴하는 메소드만 다른 서브클래스가 있으면, 그 메소드를 수퍼클래스의 필드로 바꾸고 서브클래스를 제거하라.

조건문의 단순화
  • Decompose Conditional(276) - 복잡한 조건문(if-then-else)이 있는 경우, 조건, then 부분, 그리고 else 부분에서 메소드를 추출하라.
  • Consolidate Conditional Expression(278) - 같은 결과를 초래하는 일련의 조건 테스트가 있는 경우, 그것을 하나의 조건 식으로 결합하여 뽑아내라.
  • Consolidate Duplicate Conditional Fragments(281) - 동일한 코드 조각이 조건문의 모든 분기 안에 있는 경우, 동일한 코드를 조건문 밖으로 옮겨라.
  • Remove Control Flag(283) - 일련의 boolean 식에서 컨트롤 플래그 역할을 하는 변수가 있는 경우, break 또는 return 을 대신 사용하라.
  • Replace Nested Conditional with Guard Clauses(288) - 메소드가 정상적인 실행 경로를 불명확하게 하는 조건 동작을 가지고 있는 경우, 모든 특별한 경우에 대해서 보호절(guard clause)을 사용하라.
  • Replace Conditional with Polymorphism(293) - 객체의 타입에 따라 다른 동작을 선택하는 조건문을 가지고 있는 경우, 조건문의 각 부분을 서브클래스에 있는 오버라이딩 메소드로 옮겨라. 그리고 원래 메소드를 abstract로 만들어라.
  • Introduce Null Object(298) - null 체크를 반복적으로 하고 있다면, null 값을 null 객체로 대체하라.
  • Introduce Assertion(306) - 코드의 한 부분이 프로그램의 상태에 대하여 어떤 것을 가정하고 있으면, assertion을 써서 가정을 명시되게(explicit) 만들어라.

메소드 호출의 단순화
  • Rename Method(313) - 메소드의 이름이 그 목적을 드러내지 못하고 있다면 메소드의 이름을 바꿔라.
  • Add Parameter(316) - 어떤 메소드가 그 메소드를 호출하는 부분에서 더 많은 정보를 필요로 한다면, 이 정보를 넘길 수 있는 객체에 대한 파라미터를 추가하라.
  • Remove Parameter(318) - 파라미터가 메소드 몸체에서 더 이상 사용되지 않는다면, 그 파라미터를 제거하라.
  • Separate Query from Modifier(320) - 값을 리턴할 뿐만 아니라 객체의 상태도 변경하는 메소드를 가지고 있는 경우, 두 개의 메소드를 만들어서 하나는 값을 리턴하는 역할을 하고, 하나는 객체의 상태를 변경하는 역할을 하게 하라.
  • Parameterize Method(325) - 몇몇 메소드가 메소드 몸체에 다른 값을 포함하고 있는 것을 제외하고는 비슷한 일을 하고 있다면, 다른 값을 파라미터로 넘겨 받는 하나의 메소드를 만들어라.
  • Replace Parameter with Explicit Methods(327) - 파라미터의 값에 따라서 다른 코드를 실행하는 메소드가 있다면, 각각의 파라미터 값에 대한 별도의 메소드를 만들어라.
  • Preserve Whole Object(331) - 어떤 객체에서 여러 개의 값을 얻은 다음 메소드를 호출하면서 파라미터로 넘기고 있다면, 대신 그 객체를 파라미터로 넘겨라.
  • Replace Parameter with Method(335) - 객체가 메소드를 호출한 다음, 결과를 다른 메소드에 대한 파라미터로 넘기고 있다. 수신자(Receiver - 파라미터를 넘겨 받는 메소드) 또한 이 메소드를 호출할 수 있다면, 그 파라미터를 제거하고 수신자가 그 메소드를 호출하도록 하라.
  • Introduce Parameter Object(339) - 자연스럽게 몰려다니는 파라미터 그룹을 가지고 있다면, 그것들을 객체로 바꾸어라.
  • Remove Setting Method(344) - 어떤 필드가 객체 생성시에 값이 정해지고 그 이후에는 변경되지 않아야 한다면, 그 필드 값을 설정하는 모든 메소드를 제거하라.
  • Hide Method(348) - 메소드가 다른 클래스에서 사용되지 않는다면, 그 메소드를 private으로 만들어라.
  • Replace Constructor with Factory Method(350) - 객체를 생성할 때 단순히 생성하는 것 이외에 다른 작업도 하고 있다면, 생성자를 팩토리 메소드로 대체하라.
  • Encapsulate Downcast(355) - 메소드가 그 호출부에서 다운캐스트(downcast) 될 필요가 있는 객체를 리턴하고 있다면, 다운캐스트 하는 것을 메소드 안으로 옮겨라.
  • Replace Error Code with Exception(357) - 메소드가 에러를 나타내는 특별한 코드를 가지고 있다면, 대신 예외를 던져라.
  • Replace Exception with Test(363) - 호출부에서 먼저 검사할 수 있는 조건에 대해 예외를 던지고 있다면, 호출부가 먼저 검사하도록 바꿔라.

일반화 다루기
  • Pull Up Field(368) - 두 서브클래스가 동일한 필드를 가지고 있다면, 그 필드를 수퍼클래스로 옮겨라.
  • Pull Up Method(370) - 동일한 일을 하는 메소드를 여러 서브클래스에서 가지고 있다면, 이 메소드를 수퍼클래스로 옮겨라.
  • Pull Up Constructor Body(373) - 서브클래스들이 대부분 동일한 몸체를 가진 생성자를 가지고 있다면, 수퍼클래스에 생성자를 만들고 서브클래스 메소드에서 이것을 호출하라.
  • Push Down Method(376) - 수퍼클래스에 있는 동작이 서브클래스 중 일부에만 관련되어 있다면, 그 동작을 관련된 서브클래스로 옮겨라.
  • Push Down Field(377) - 어떤 필드가 일부 서브클래스에 의해서만 사용되고 있다면, 그 필드를 관련된 서브클래스로 옮겨라.
  • Extract Subclass(378) - 어떤 클래스가 일부 인스턴스에 의해서만 사용되는 기능을 가지고 있다면, 기능의 부분집합을 담당하는 서브클래스를 만들어라.
  • Extract Superclass(384) - 비슷한 메소드와 필드를 가진 두 개의 클래스가 있다면, 수퍼클래스를 만들어서 공통된 메소드와 필드를 수퍼클래스로 옮겨라.
  • Extract Interface(389) - 여러 클라이언트가 한 클래스 인터페이스의 동일한 부분 집합을 사용하고 있거나, 두 클래스가 공통된 인터페이스를 가지는 부분이 있다면, 그 부분 집합을 인터페이스로 뽑아내라.
  • Collapse Hierarchy(392) - 수퍼클래스와 서브클래스가 별로 다르지 않다면, 그것들을 하나로 합쳐라.
  • Form Template Method(393) - 각각의 서브클래스에, 동일한 순서로 비슷한 단계를 행하지만 단계가 완전히 같지는 않은 두 메소드가 있다면, 그 단계를 동일한 시그니처를 가진 메소드로 만들어라. 이렇게 하면 원래의 두 메소드는 서로 같아지므로, 수퍼클래스로 올릴 수 있다.
  • Replace Inheritance with Delegation(401) - 서브클래스가 수퍼클래스 인터페이스의 일부분만 사용하거나 또는 데이터를 상속 받고 싶지 않은 경우, 수퍼클래스를 위한 필드를 만들고 메소드들이 수퍼클래스에 위임하도록 변경한 후 상속 관계를 제거한다.
  • Replace Delegation with Inheritance(404) - 위임을 사용하고 있는데, 전체 인터페이스에 대해 간단한 위임을 자주 작성하고 있다면, 위임하는 클래스를 대리객체의 서브클래스로 만들어라.

대규모 리팩토링
  • Tease Apart Inheritance(410) - 두 가지 작업을 한번에 처리하는 상속 구조가 있는 경우, 두개의 상속 구조를 만들고 하나가 다른 하나를 호출하도록 위임을 사용하라.
  • Convert Procedural Design To Objects(416) - 절차적 스타일로 작성된 코드가 있다면, 데이터 레코드를 객체로 바꾸고, 동작을 쪼개서 객체로 옮겨라.
  • Separate Domain from Presentation(418) - 도메인 로직을 포함하고 있는 GUI 클래스를 가지고 있다면, 도메인 로직을 분리하여 도메인 클래스를 만들어라.
  • Extract Hierarchy(423) - 너무 많은 작업을 하거나 또는 부분적으로라도 많은 조건문이 있는 클래스에 대해서는, 각각의 서브클래스가 특정 작업을 담당하도록 클래스의 상속 구조를 만들어라.

'생각하는개발 > 리팩토링' 카테고리의 다른 글

리팩토링 기술 업그레이드 하기  (0) 2010.09.27
Form Template Method  (0) 2010.09.27
Extract Super Class  (0) 2010.09.27
Introduce Parameter Object  (0) 2010.09.27
Extract Class  (0) 2010.09.27
Posted by 모과이IT
,

필자의 프로젝트 팀내 감금 사태로 인해 12월호 특집 기사에 2페이지가 부족하니 채워달라는 긴급 SOS 연락을 받고 급하게 작성해서 보낸 기사입니다. 옛날에 있던 회사 동료 분이랑 오랜만에 넷미팅과 구글토크를 이용해서 TDD 수련을 위한 짝프로그래밍 놀이를 했더니 이 글이 생각나서 올려봅니다.

테스트와 리팩토링
"리팩토링을 하고자 할때 견고한 테스트는 없어서는 안 될 필수조건이다.“

마틴 파울러는 리팩토링을 할 때 반드시 기억해야 할 기본 명제로 아래의 2가지 항목을 말했다.

(1) 리팩토링은 소프트웨어의 기능을 변경하지 않는다.
(2) 제대로 동작하지 않는 코드는 리팩토링하지 않는다.

이를 통해 기능을 추가하는 작업과 기능의 추가 없이 내부 구조를 개선하는 작업을 나누어 진행해야 함을 알 수 있다. 여기서 중요한 것은 이러한 기본 명제에 의해 파생되는 2가지 요구사항이다.

(1) 내가 추가한 기능이 제대로 동작한다는 것을 확인한 다음에야 리팩토링을 수행할 것인지에 대한 여부를 결정할 수 있다.
(2) 리팩토링을 한 이후에 소프트웨어는 리팩토링을 하기 전과 비교해서 기능 변경이 없어야 한다.

어떻게 추가한 기능이 개발자의 의도대로 바르게 동작함을 확인할 수 있을까? 어떻게 리팩토링 과정에서 기존에 잘 동작하던 기능들이 훼손되지 않았음을 보장할 수 있을까? 이 질문에 대한 답은 바로 테스트이다. 테스트는 리팩토링과 따로 떼어 생각할 수 없는 필수적인 행위인 것이다.


그럼에도 불구하고 리팩토링을 실천하고 있다고 주장하는 많은 개발자들 중에 테스트의 가치를 진심으로 인정하는 사람을 만나기는 쉽지 않다. 리팩토링 카탈로그에 소개된 리팩토링 기법들을 실천함으로써 “훌륭한 개발자는 사람이 이해할 수 있는 코드를 짠다”는 가치를 추구하고 있는 개발자라면, 일정 부족이나 기능추가 외에는 가치를 이해하지 못하는 관리자를 핑계로 테스트 개발을 소홀히 해서는 안된다. 테스트가 없는 리팩토링은 최소한의 전제 조건도 만족시키지 못하는 반쪽짜리에 불과하기 때문이다.

하지만 이미 구현을 끝내고 직접 실행시켜보는 방법으로 동작을 확인한 코드에 대해 테스트를 작성하다 보면 내가 왜 이 짓을 하고 있을까 하는 의구심이 들기 마련이다. 그래서 테스트를 개발한다 하더라도 형식적인 테스트에서 그치고 만다. 이러한 상황에서 벗어나기 위한 최선의 방법은 구현에 앞서 테스트를 먼저 개발하는 테스트 주도 개발(Test-Driven Development)을 도입하는 것이다. 테스트 주도 개발을 가끔 테스트 주도 리팩토링(Test-Driven Refactoring)이라고 부르는 이유는 이처럼 둘의 궁합이 잘 맞기 때문이다. 리팩토링에서 테스트는 강력한 버그 탐지기의 역할을 한다. 이런 테스트는 개발을 진행하는 전 주기에 걸쳐서 자주 실행된다. 따라서 자동화된 빌드 도구를 활용해서 모든 테스트를 자동화하는 것이 권장된다. 테스트 주도 개발과 자동화된 테스트는 여러분이 작성한 코드에 대해 자신감을 불어넣고, 리팩토링이라는 도구를 마음껏 휘두를 수 있도록 도와주는 든든한 후원자가 될 것이다.

안타깝게도 나쁜 냄새 목록과 리팩토링 카탈로그를 달달 외운다고 해서 리팩토링을 숙달했다고 말하기 힘든 것처럼, 테스트 주도 개발도 원리와 기술을 익히기는 쉽지만 실전에서 적용하기 위해서는 오랜 시간의 훈련을 필요로 한다. 아직 테스트 주도 개발을 시도해보지 않았던 개발자라면 모든 것을 한꺼번에 이루어 내리라는 기대는 버리는 것이 좋다. 1일 1테스트 만들기, 토요일 오후를 테스트 작성 훈련에 할애하기와 같이 조금씩 자신의 테스트 관련 기술을 향상시키는 방법을 고민하고 실천하기를 권한다. 아래 그림에서 보듯 일일이 수작업으로 동작을 확인하던 시절과 비교하면, 자동화된 테스트 작성을 위해 투자한 시간을 분명히 보상받을 수 있을 것이다.


디자인 패턴과 리팩토링
“패턴은 우리가 있고 싶은 곳이고, 리팩토링은 그곳에 이르는 방법이다.”

마틴 파울러가 저술한 리팩토링 서적은 나쁜 디자인을 좋은 디자인으로 바꿔주는 마법과 같은 기법들을 카탈로그 방식으로 잘 분류해서 정리해 주고 있다. 하지만 대부분 이름 변경이나 메소드 추출과 같은 저수준의 리팩토링을 다루고 있기 때문에, 풍부한 경험을 갖추지 못한 개발자들에게는 최선의 설계를 이끌어낼 수 있는 장치가 부족한 것도 사실이다. 이를 보완할 수 있도록 다양한 디자인 패턴이 존재하지만, 디자인 패턴에 집중하다 보면 지속적이고 발전적인 설계라는 리팩토링의 개념에서 벗어나기 쉽다. 패턴의 강력함에 빠져 간단하게 작성할 수 있는 코드를 괜히 복잡하게 만든다거나, 미래의 요구사항까지 모두 예측해서 미리부터 필요이상으로 과도한 설계를 하게 되는 경우가 있는 것이다.

리팩토링을 도입 하자니 패턴을 적용한 것과 같은 품질의 설계에 이르지 못하고, 패턴을 도입하자니 패턴 만능주의에 빠져 버리는 것이 두려운 개발자들을 위해 또 하나의 좋은 서적이 세상에 모습을 드러냈다. 그게 바로 “패턴을 활용한 리팩토링(Refactoring to Patterns)”이라는 책이다. 이 책은 리팩토링에서 소개한 저수준의 리팩토링 기법을 복합적으로 활용해서, 패턴 중심의 사전 설계를 했을 때와 같은 수준의 설계로 코드를 리팩토링 해 나가는 기법을 소개하고 있다. 동기->절차->예제로 구성된 각각의 복합 리팩토링 기법을 리팩토링 서적과 같은 카탈로그 방식으로 정리하고 있다는 것도 장점이다. 디자인 패턴과 리팩토링을 공부했지만 아직 그 두 가지 기술이 가지는 장점을 하나로 합칠 만큼의 내공을 갖추지 못했다면, “패턴을 활용한 리팩토링” 서적에 소개된 기법들을 훈련하고 적용해보기를 권한다.

프로젝트 팀의 리팩토링

팀 단위 개발을 진행할 때의 리팩토링은 몇 가지 주의할 점이 있다.

1. 리팩토링을 지원하는 툴이 표준 개발 환경에 포함되도록 하라.
툴의 도움없이 리팩토링을 수행하는 것은 지극히 위험하고 따분한 작업이다. 리팩토링을 수행하기 위해 필요한 최소한의 도구는 리팩토링 기능이 추가된 개발환경(예: 이클립스)과 최악의 경우 이전 상태로 되돌아 가기 위한 버전관리시스템(예: CVS)이다. 이러한 도구의 도움 없이 수작업으로 리팩토링 하는 것은 예상치 못한 버그를 만들어 낼 수 있으므로 피하는 것이 좋다.

2. 이름을 변경할 때는 반드시 코딩표준안을 참조하라.
이름 변경(Rename)은 단순하지만 가장 효과적인 리팩토링 방법이다. 개발자의 의도가 충분히 드러나도록 이름을 지어두는 것 만으로도 문제의 복잡성을 낮출 수 있다. 하지만 의미있는 이름에 대한 개발자의 생각이 서로 다르기 때문에, 자신이 선호하는 이름이 아니라고 해서 이름을 변경해 버리는 것은 팀 전체로 볼 때 오히려 혼란을 초래할 수 있다. 당신이 어떤 팀에 소속되어 일하고 있다면, 그 팀의 이름 작성 규칙을 반드시 숙지하고, 그 규칙이 허용하는 범위 안에서만 이름을 변경해야 한다.

3. 공개된 인터페이스를 변경할 경우 호환성 여부를 확인하라.
외부에서 참조하는 경우가 많고 버전까지 달라지는 경우라면 공개된 인터페이스에 대한 리팩토링을 신중하게 수행할 필요가 있다. 꼭 필요하다고 판단해서 메소드 명이나 파라미터를 변경했다면, 기존 시스템과의 호환성이 깨어지지 않도록 주의해야 한다. 호환성을 해치지 않기 위해서는 이전에 공개된 인터페이스와 그 구현을 미사용(Deprecated) 형태로 남겨둬야 하는데, 그런 API가 많아지면 유지보수가 힘들어진다. 따라서 공개 인터페이스는 이후의 확장이나 변경에 영향을 받지 않도록 초기 설계를 보다 신중하게 할 필요가 있다.

4. 마감일이 가까워 오면 리팩토링 시기를 미뤄라.
테스트 등을 통해 오류를 자동으로 검출해 낼 수 있다고 하더라도, 리팩토링으로 인해 발생한 오류를 수정할 시간이 필요해진다. 일주일 뒤에 중요한 보고회가 예정되어 있거나, 시스템 마감일이 가까워진 시점에서 리팩토링을 수행하는 것은 무모한 판단일 수 있다. 이럴 경우는 미진한 설계를 개선시키고자 하는 욕구를 잠시 억눌러 두고, 그 시간을 시스템 안정화를 위해 사용하는 것이 더 좋다.

5. 리팩토링을 포기하고 재작성하는 것이 옳을 때도 있다.
가끔 개발과 관련된 근거 문서도 없고, 어떤 것들이 제대로 동작하는 지를 확인할 수 있는 테스트 코드도 전혀 없고, 컴퓨터만이 겨우 해독해 낼 수 있는 특수한 스파게티 코드로 둘러싸여진 ‘괴물’과 같은 프로그램을 접할 때가 있다. 그런 코드를 리팩토링 하는 것은 설사 그것이 가능하다 하더라도 개발자란 직업을 포기하고 싶을 만큼의 강력한 스트레스를 유발하므로 피하는 것이 옳다. 그런 코드를 만나면 코드 작성자에게 전달되게끔 강력한 포스로 한바탕 욕을 퍼붙고 나서, 그로 인해 얻은 에너지를 토대로 처음부터 재작성할 결심을 굳히도록 하자.

'생각하는개발 > 리팩토링' 카테고리의 다른 글

리팩토링 카달로그 정리  (0) 2010.11.17
Form Template Method  (0) 2010.09.27
Extract Super Class  (0) 2010.09.27
Introduce Parameter Object  (0) 2010.09.27
Extract Class  (0) 2010.09.27
Posted by 모과이IT
,

각각의 서브 클래스에, 동일한 순서로 비슷한 단계를 행하지만 단계가 완전히 같지는 않은 두 메소드가 있다면,
그 단계를 동일한 시그너처를 가진 메소드로 만들어라.
이렇게 하면 원래의 두 메소드는 서로 같아지므로, 수퍼클래스로 올릴 수 있다.

동기
상속을 통해서 중복을 제거할 때 서브 클래스들에 있는 비슷한 메소드들을 본질적인 차이점만을 남겨놓고 중복을 제거할 수 있다. 이와 관련된 패턴으로 Template Method 패턴이 있다.

절차
1. 메소드를 분해해서 동일 한 부분과 다른 부분을 나눈다.
2. 동일한 부분을 Pull Up Method를 사용하여 수퍼클래스로 올린다.
3. 다른 부분을 정의한 메소드를 수퍼클래스에서 추상 메소드로 선언한다.
4. 서브 클래스에는 다른 부분을 정의한 메소드를 제외하고 모두 제거한다.
5. 각 단계마다 컴파일, 테스트를 한다.

예제
예제 코드는 Extract Superclass에서 사용했던 코드를 그대로 사용합니다.
0. Extract Superclass로 상위 클래스를 만듭니다.
사용자 삽입 이미지

1. 메소드를 고정적인 부분과 유동적인 부분으로 분해합니다.
- before
사용자 삽입 이미지
- Alt + Shift + M 을 사용하여 메소드로 빼낼 수 있습니다.
사용자 삽입 이미지
- after
사용자 삽입 이미지

2. Pull Up Method를 사용하여 공통된 부분을 상위 클래스로 옮깁니다.
- 이 때 Alt + Shift + T 로 리팩토링 메뉴를 열고 Pull Up Method를 사용할 수 있습니다.
사용자 삽입 이미지

3. 수퍼클래스에서 추상 메소드로 변경되는 내용을 지닌 메소드를 만들어 줍니다.
- 변경되는 부분의 메소드가 올라가지 않았기 때문에 수퍼클래스에서 에러가 납니다. 에러가 발생한 라인에서 퀵 픽스(Ctrl + 1)를 사용하여 만들 수 있습니다.
사용자 삽입 이미지
- abstract protected void doService(Task task);
메소드에 abstract를 붙여주면 클래스도 추상 클래스가 되어야 하며 그럼 추상 클래스의 이름 앞에도 Abstract를 붙여주는 것이 좋겠습니다. Package Explorer에서 클래스르 선택하고 F2 를 클릭하여 클래스 이름을 변경할 수 있습니다.
사용자 삽입 이미지
4. 서브클래스를 정리합니다.
- 현재 작업한 서브 클래스는 전부 변경이 되었겠지만 다른쪽 서버클래스는 별다른 작업이 없었기 때문에 공통 부분을 제거하고 추상 메소드를 구현해 줍니다. 이때도 퀵 픽스를 사용하면 편리합니다.

'생각하는개발 > 리팩토링' 카테고리의 다른 글

리팩토링 카달로그 정리  (0) 2010.11.17
리팩토링 기술 업그레이드 하기  (0) 2010.09.27
Extract Super Class  (0) 2010.09.27
Introduce Parameter Object  (0) 2010.09.27
Extract Class  (0) 2010.09.27
Posted by 모과이IT
,

비슷한 메소드와 필드를 가진 두 개의 클래스가 있다면, 슈퍼클래스를 만들어서 공통된 메소드와 필드를 수퍼클래스로 옮겨라.
동기
중복을 제거 할 수 있습니다.

절차
1. 빈 수퍼 클래스를 만들고 원래 클래스들이 이것을 상속하도록 한다.
2. Pull Up Filed, Pull Up Method, Pull Up Contruct Body들을 사용하여 공통 요소들을 옮긴다.
3. 옮길 때 마다 테스트를 한다.
4. 공통된 부분이 있다면 Extract Method를 사용한 뒤에 Pull Up Method를 사용할 수 있다.

예제
리팩터링 대상이 된 두 개의 클래스들
- AddController.java

less..

public class AddController extends SimpleFormController{

 

       private TaskService taskService;

 

       public void setTaskService(TaskService taskService) {

             this.taskService = taskService;

       }

 

       public AddController() {

             setCommandClass(Task.class);

             setCommandName("task");

             setFormView("task/add");

             setSuccessView("task/list");

       }

 

       @Override

       protected ModelAndView onSubmit(Object command) throws Exception {

             Task task = (Task)command;

             task.setEnrolled(new Date());

             taskService.add(task);

             return new ModelAndView(getSuccessView())

                    .addObject("taskList", taskService.getTaskList());

       }

}

less..

- UpdateController.java

less..


public class UpdateController extends SimpleFormController {

 

       private TaskService taskService;

 

       public void setTaskService(TaskService taskService) {

             this.taskService = taskService;

       }

 

       public UpdateController() {

             setCommandClass(Task.class);

             setCommandName("task");

             setFormView("task/update");

             setSuccessView("task/list");

       }

 

       @Override

       protected Object formBackingObject(HttpServletRequest request) throws Exception {

             Integer id = Integer.parseInt(request.getParameter("id"));

             Task task = taskService.get(id);

             return task;

       }

 

       @Override

       protected ModelAndView onSubmit(Object command) throws Exception {

             Task task = (Task)command;

             task.setEnrolled(new Date());

             taskService.update(task);

             return new ModelAndView(getSuccessView())

                    .addObject("taskList", taskService.getTaskList());

       }

}

less..


리팩터링
1. 상위 클래스를 만들고 각각의 클래스들이 그 클래스를 상속 받도록 한다.
Eclipse를 사용하신다면 리팩터링 대상이 된 클래스에서 Alt + Shift + T를 클릭하여 리팩터링 메뉴를 띄우고 Extract Super Class를 선택할 수 있습니다.
사용자 삽입 이미지

2. 상위 클래스의 이름을 설정하고 상위로 빼낼 멤버들을 선택합니다.
사용자 삽입 이미지

3. Eclipse의 리팩터링 기능을 사용하여 만들어진 클래스를 상속 받도록 수정 되었습니다.
사용자 삽입 이미지

4. 다른 클래스는 수동으로 상속 받고 있는 클래스를 수정해 주거나 상속 하도록 코드를 수정하고 공통 요소를 하나 둘 빼내거나 Pull Up XXX 리팩토링 기능을 사용하여 옮길 수 있습니다.

참조 : Refactoring

'생각하는개발 > 리팩토링' 카테고리의 다른 글

리팩토링 기술 업그레이드 하기  (0) 2010.09.27
Form Template Method  (0) 2010.09.27
Introduce Parameter Object  (0) 2010.09.27
Extract Class  (0) 2010.09.27
Pull Up Method  (0) 2010.09.27
Posted by 모과이IT
,
참조 : http://www.refactoring.com/catalog/introduceParameterObject.html

여러개의 파라미터를 가지는 메소드의 경우 그 일련의 파라미터들을 가지는 새로운 객체 타입을 받도록 변경할 수 있습니다.

사용자 삽입 이미지

Eclipse의 refactoring 기능 중에 저런 리팩터링을 지원하는 것이 없는지 찾아봤지만 못찾았습니다. 있으면 좋으련만...
http://dev.eclipse.org/mhonarc/lists/eclipse-pmc/msg00188.html
http://download.eclipse.org/eclipse/downloads/drops/S-3.3M7-200705031400/eclipse-news-M7.html
위 글들을 보니 Eclipse3.3의 refactoring 기능에 추가해 줄 것 같습니다.

하나의 메소드 안에 뭉탱이로 들어가는 인자들이 있다면 그 인자들끼리 관계가 밀접할 수 있으며 해당 객체에서 따로 빼내는 것이 애플리케이션을 더 유연하게 만들 수 있습니다.

메소드에 넘겨줄 인자가 단순해집니다.

'생각하는개발 > 리팩토링' 카테고리의 다른 글

Form Template Method  (0) 2010.09.27
Extract Super Class  (0) 2010.09.27
Extract Class  (0) 2010.09.27
Pull Up Method  (0) 2010.09.27
Extract Method  (0) 2010.09.27
Posted by 모과이IT
,
두 개의 클래스가 해야 할 일을 하나의 클래스가 하고 있는 경우,
새로운 클래스를 만들어서 관련 있는 필드와 메소드를 예전 클래에서 새로운 클래스로 옮겨라.

동기

클래스가 너무 많은 메소드와 데이터를 가지고 있어서 쉽게 이해할 수 없는 경우가 있다. 이때 데이터의 부분집합과 메소드의 부분집합이 몰려 다닌다면 새로운 클래스로 분리할 수 있다는 좋은 신호다.

절차
  1. 클래스의 책임을 어떻게 나눌지를 결정하라.
  2. 분리된 책임을 떠맡을 새로운 클래스를 만든다.
    • 책임을 분리한 후 이전 클래스의 책임이 이름과 더 이상 맞지 않는다면, 이전 클래스의 이름을 변경한다.
  3. 이전 클래스에서 새로 만든 클래스에 대한 링크를 만든다.
    • 양방향 링크가 필요할지도 모른다. 그러나 필요해지기 전에는 새로 만든 클래스에서 이전 클래스로 링크를 만들지 마라.
  4. 옮기고자 하는 각각의 필드에 대해 Move Field를 사용한다.
  5. 각각의 필드를 옮길 때마다 컴파일, 테스트를 한다.
  6. Move Method를 사용해서 이전 클래스에서 새로 만든 클래스로 메소드를 옮긴다. 저수준 메소드(호출하기 보다는 호출되는 메소드)부터 시작해서 짐점 고수준의 메소드에 적용한다.
  7. 각각의 메소드를 옮길 때마다 컴파이르 테스트를 한다.
  8. 각 클래스를 검토하고, 인터페이스를 줄인다.
    • 양방향 링크를 가지고 있다면, 단바양 링크로 만들 수 있는지 알아본다.
  9. 새로운 클래스를 공개할지 결정한다. 새로운 클래슬 공개하기로 결정했다면, 참조 객체로 드러낼지 또는 불변성 값 객체(immutable value object)로 드러낼지를 결정한다.
예제

닫기


public class Person {

   private String name;
   private String officeAreaCode;
   private String officeNumber;

   public String getName() {
       return name;
   }

   public String getTelephoneNumber() {
       return ("(" + officeAreaCode + ") " + officeNumber);
   }

   public String getOfficeAreaCode() {
       return officeAreaCode;
   }

   public void setOfficeAreaCode(String officeAreaCode) {
       this.officeAreaCode = officeAreaCode;
   }

   public String getOfficeNumber() {
       return officeNumber;
   }

   public void setOfficeNumber(String officeNumber) {
       this.officeNumber = officeNumber;
   }
}

닫기



위 예제 코드를 보시면 officeAreaCode와 officeNumber 데이타가 몰려 다니고 거기에 따른 getter와 setter들이 몰려 다니는 것을 볼 수 있습니다. 이것을 TelephoneNumber라는 class로 따로 뽑아내기 위해 먼저 TelephoneNumber 클래스를 만들고 Move Field를 사용합니다.

이클립스에서 클래스를 만들고 이동하고자 하는 필드를 선택하고 Alt + Shift + v 를 클릭하면 다음과 같은 팝업창이 뜨게 되고 이동시킬 클래슬 선택할 수 있다.


하지만 단순히 데이터만 옮겨 줄뿐 거기에 딸린 getter와 setter를 같이 옮겨 주지 않는 것은 조금 아쉬웠다. 이번에는 옮기고 싶은 것을 통째로 드래그를 해보았다. 그러나 메소드를 옮기는 일이 쉽지가 않은 듯하다.

결국 잘라내기 붙여넣기를 사용하여 아래와 같이 두개의 클래로 분리하였다.

닫기


public class Person {

   private String name;
   private TelephoneNumber officeTelephone;

   public String getName() {
       return name;
   }

   public String getTelephoneNumber() {
       return officeTelephone.getTelephoneNumber();
   }
}

닫기


닫기


public class TelephoneNumber {

   private String areaCode;
   private String number;

   public String getTelephoneNumber() {
       return ("(" + areaCode + ") " + number);
   }

   public String getAreaCode() {
       return areaCode;
   }

   public void setAreaCode(String officeAreaCode) {
       this.areaCode = officeAreaCode;
   }

   public String getNumber() {
       return number;
   }

   public void setNumber(String officeNumber) {
       this.number = officeNumber;
   }

}

'생각하는개발 > 리팩토링' 카테고리의 다른 글

Extract Super Class  (0) 2010.09.27
Introduce Parameter Object  (0) 2010.09.27
Pull Up Method  (0) 2010.09.27
Extract Method  (0) 2010.09.27
Substitute Algorithm (대체 알고리즘)  (0) 2010.09.27
Posted by 모과이IT
,
동일한 일을 하는 메소드를 여러 서브클래스에서 가지고 있다면, 이 메소드를 수퍼클래스로 옮겨라.


동기
  • 중복된 동작을 제거해야 하기 때문입니다. 한쪽은 바꿨는데 다른 쪽은 그대로 라면 수정하기도 번거롭고 나중에 버그가 될 가능성이 높을 것입니다.
Pull Up Method가 필요한 경우는 서브클래스에 있는 메소드가 수퍼클래스에 있는 메소드를 오버라이드하는데 여전히 같은 일을 하는 때입니다.
이 때 성사긴 것은 서브클래스의 메소드에서 수퍼클래스에 없는 메소드나 변수를 참조할 때입니다. 이 때는 메소드도 같이 일반화하거나 수퍼클래스에 추상 메소드를 만들 수 있습니다. (이럴 때 메소드의 시그너쳐를 변경하거나 위임 메소드(?)를 만들어야 할 지도 모른다고 합니다.)
비슷하지만 동일한 메소드가 아닐때는 Form Template Method를 사용할 수 있을지도 모릅니다.

절차
  1. 메소드들을 조사해서 그것이 동일한지 확인한다.
    • 만약 같은 일을 하지만 코드가 다른경우 메소드들 중 하나에 Substitute Algorithm을 사용하여 코드가 동일하게 만들어라.
  2. 메소드가 서로 다른 시그너쳐를 가지고 있다면, 수퍼 클래스에서 사용하고 싶은 시그너처로 바꾼다.
  3. 수퍼클래스에 새로운 메소드를 만들고, 메소드들 중 하나의 몸체를 새로운 메소드에 복사해서 적절히 수정한 후 컴파일한다.
    • 그 메소드가 수퍼클래스에 없는 메소드를 호출한다면, 수퍼클래스에 추상 메소드를 만들어라.
    • 수퍼클래스에 없는 필드를 사용한다면, Pull Up Field 또는 Self Encapsulate Field를 사용하고 추상 get메소드를 선언하여 사용하라.
  4. 서브클래스 중 하나를 골라서 메소드를 삭제한다.
  5. 컴파일, 테스트를 한다.
  6. 수퍼 클래스 메소드만 남을 떄까지 서브클래 메소드를 삭제하고 테스트를 계속한다.
  7. 필요한 타입을 수퍼클래스로 변경할 수 있는지 살펴보기 위해서 이 메소드의 호출부를 살펴본다.
예제

두 개의 서브클래스(RegularCustomer, PreferredCustomer)를 가진 Customer 클래스가 있다. 그리고 두 서브클래스에 있는 createBill 메소드는 둘다 다음과 같은 코드로 동일하다.

void createBill(Date date) {
   double chargeAmount = chatgeFor(lastBillDate, date);
   addBill(date, charge);
}


색칠 된 부분의 코드가 모두 중복되고 있지만 둘 중에 녹색으로 칠해진 chargeFor 메소드는 위로 복사해서 옮길 수가 없습니다. 클래스 몸체가 서브클래스마다 다르기 때문입니다. 이때는 수퍼클래스에 추상 메소드로 선언합니다.


addBill 메소드는 수퍼클래스로 옮겼고 chargeFor 메소드는 수퍼클래스에 추상 메소드로 선언했습니다.(UML 표기상 추상 메소드는 이탤릭체로 표기합니다. 그리고 추상 메소드를 가진 클래스는 추상 클래스가 되기 때문에 클래스 이름도 이탤릭체로 표기했습니다.)

'생각하는개발 > 리팩토링' 카테고리의 다른 글

Introduce Parameter Object  (0) 2010.09.27
Extract Class  (0) 2010.09.27
Extract Method  (0) 2010.09.27
Substitute Algorithm (대체 알고리즘)  (0) 2010.09.27
3장 코드 속의 나쁜 냄새 - 긴 메소드 (4/4장)  (0) 2010.09.27
Posted by 모과이IT
,
그룹으로 함꼐 묶을 수 있는 코드 조각이 있으면 코드의 목적이 잘 드러나도록 메소드의 이름을 지어 별도의 메소드로 뽑아낸다.


동기

지나치게 긴 메소드나 주석이 필요한 코드를 보면 그 부분을 하나의 메소드로 뽑는다.(Martin Fowler 曰).
  1. 메소드가 잘게 쪼개져 있을 때 다른 곳에서 사용하기 좋다.
  2. High-level의 메소드를 보면 주석을 읽는 것 같은 느낌이 들도록 할 수 있다.
  3. 오버라이드 하는 것도 훨씬 쉽다.
작은 메소드들은 작명을 잘했을 때 그 진가를 드러내므로 이름을 잘 짓도록 하자.

절차
  1. 메소드를 새로 만들고, 무엇을 하는지를 나타내도록 이름을 정한다.
  2. 원래 메소드에서 뽑아내고자 했던 부분을 복사하여 새 메소드로 옮긴다.
  3. 원래 메소드에서 사용되고 있는 지역변수가 뽑아낸 코드에 있는지 확인한다. 있으면 새로운 메소드의 임시변수로 선언한다.
  4. 뽑아낸 코드 안에서 지역변수의 값이 변화는지 확인한다. 만약에 하나의 지역변수만 수정 된다면, 뽑아낸 코드를 질의(query)로 보고, 수정된 결과를 관련된 변수에 대입할 수 있는지 본다. 이렇게 하는 것이 이상하거나, 값이 수정되는 지역변수가 두개 이상 있다면 쉽게 메소드로 추출할 수 없는 경우이다. 이럴 때는 Split Temporary Variable을 사용한 다음 다시 시도해야 한다. 임시변수는 Replace Temp with Query로 제거할 수 있다.
  5. 뽑아낸 코드에서 읽기만 하는 코드는 파라미터로 넘긴다.
  6. 지역변수와 관련된 사항을 다룬 후에는 컴파일을 한다.
  7. 원래 메소드에서 새로 만든 메소드를 호출하도록 바꾼다.
  8. 컴파일과 테스트를 한다.
예제 : 지역변수가 없는 경우
위 코드에서 처음에 수정할 부분은 노란색으로 표시된 부분입니다. 매우 단순한 경우이기 때문에 잘라서 새로운 메소드로 붙여넣고 저 노란 부분에서 새로운 메소드를 호출하면 되겠습니다.

이클립스에서 리팩토링 할 부분을 드래그 한 상태에서 Alt + Shift + m 을 클릭합니다. 그리고 새로운 메소드의 이름을 적어 줍니다.
그리고 OK 버튼을 클릭하면...
짠... 멋지게 처리해 주는 군요.. 주석만 빼고. ^^; 주석은 Ctrl + d (행삭제)를 사용하여 지워줍시다.

예제 : 지역변수가 포함되어 있는 경우

지역변수가 옮겨질 코드에 들어있는 경우 중에 가장 쉬운 경우가 바로 그 지역변수를 읽기만 하는 경우입니다.  이런 경우에는 변수를 파라미터로 넘겨주면 됩니다.


이번에는 저 노란 부분을 메소드로 뽑아 낼 것입니다. 보아하니 _name이라는 변수와 outstanding이라는 변수를 사용하고 있는데 outstanding만 지역변수고 _name은 필드네요. 따라서 outstanding만 넘겨주면 되겠습니다. 메소드  이름은 printDetails로 하는군요. 이번에도 뽑아내고 싶은 부분을 드래그 하고 Alt + Shift + m ...와오..


똑똑한 이클립스... 파라미터가 필요한 줄 알고.. 그것도 딱 지역변수만.. 아 정말 똑똑하네요. 역시 OK만 누르면..


대단합니다. 이클립스.

예제 : 지역변수에 다른 값을 여러 번 대입하는 경우 (마지막입니다. :)

이 경우는 복잡한데 이런 경우 중 임시변수에 대해서만 생각을 해봅니다. (파라미터에 다른 값을 대입하는 코드가 있다면 즉시 Remove Assignments to Parameters를 적용해야 한다.)만약에 그 임시변수가 뽑아내는 코드에서만 사용이 된다면 뽑아낸 코드로 그냥 그 임시변수까지 이동하면 되겠습니다. 하지만 그 임시변수가 뽑아내는 코드 밖에서도 사용이 된다면 뽑아낸 코드에서 그 값을 리턴해 주어야 합니다.
노란 부분을 보시면 each라는 임시변수는 뽑아내게 될 코드 안에서만 사용되는 것을 볼 수 있습니다. 그렇기 때문에 그냥 이동 시키면 되겠지요

위에 보이는 노란 부분이 뽑아 낼 부분입니다. 저 중에 each는 노란 부분에서만 쓰이기 때문에 당연히 그냥 빼내는 메소드로 이동하면 되겠습니다. 하지만 문제는 outstanding인데 이 변수가 printDetails 메소드에서 사용되고 있습니다. 따라서 위의 코드에서 계산 부분의 코드를 뽑아내고 outstaning의 값을 리턴하도록 합니다.

이번에는 이클립스의 덕을 잘 못보겠더군요.

임시변수가 너무 많아 코드를 뽑아내기 어려울 때는 Replace Temp with Query를 사용하여 임시변수를 줄이고 어떻게 해봐도 여전히 난처할 때는 Replace Method with Method Object를 사용한다. 이 리팩토링은 임시변수가 얼마나 많든, 그리고 뭘 하든 상관하지 않는다.

Posted by 모과이IT
,
알고리즘을 보다 명환한 것으로 바꾸고 싶을 때는, 메소드의 몸체를 새로운 알고리즘으로 바꾼다.



위 코드를 보시면 people이라는 문자열 배열안에 Don, John, Kent라는 이름이 있는지 확인하고 만약에 있다면 해당하는 이름을 반환하는 메소드 입니다. 이것을 좀더 명확하게 표현하자면 아래와 같이 변경할 수 있습니다.


동기

어떤 것을 할 때 여러가지 방법이 있다면 그 중에 쉬운 것이 있다면 그것을 선택할 것입니다. 알고리즘도 마찬가지로 여러가지 방법 중에 좀더 명확한 방법을 선택하여야 합니다. 이 작업을 할 때는 가능한 많이 메소드를 분해해 두어야 합니다. 크고 복잡한 알고리즘을 바꾸는 것은 어렵기 때문이죠.

절차
  1. 대체 알고리즘을 준비한다. 적용하여 컴파일한다.
  2. 알고리즘을 테스트한다. 만약 결과가 같다면 작업은 끝난 것이다.
  3. 만약 결과가 같지 않다면, 테스트에서 비교하기 위해 예전의 알고리즘을 사용하여 디버깅한다.
    • 예전 알고리즘과 새 알고리즘에 대해 각각의 테스트 케이스를 실행시키고 두 결과를 본다. 이것은 어떤 테스트 케이스가 어떻게 문제를 일으키는지 찾는 데에 도움을 줄 것이다.
Posted by 모과이IT
,
긴 메소드(Long Method)

최적의 상태로 가장 오래 살아 남는 객체 프로그램은 메소드가 짧다. 인디렉션(indirection)의 이점은 모두 짧은 메소드에 의해 제공되는 것이다.

인디렉션이란?

닫기

Indirection

From Wikipedia, the free encyclopedia

Jump to: navigation, search

In computer programming, indirection is the ability to reference something using a name, reference, or container instead of the value itself.

The simplest form of indirection is the act of manipulating a value through its memory address. For example, accessing a variable through the use of a pointer. A stored pointer that exists to provide a reference to an object by double indirection is called an indirection node.

In some older computer architectures, indirect words supported a variety of more-or-less complicated addressing modes.

Object-oriented programming makes use of indirection extensively, a simple example being dynamic dispatch. Higher-level examples of indirection are the design patterns of the proxy and the proxy server. Delegation is another classic example of an indirection pattern.

In strongly-typed interpreted languages with dynamic datatypes,most variable references require a level of indirection: first the typeof the variable is checked for safety, and then the pointer to theactual value is dereferenced and acted on.

닫기


원문에는 더 많은 내용이 나와있지만 간략히 요약하면 인디렉션이란 실제 값 자체가 아닌 그것을 참조하는 다른 이름을 사용하는 기능을 말한다는 것 같다. 이렇게 함으로써 프록시와 dynamic dispatch도 가능한 듯하다.

책의 82쪽에는 이런말이 있다.

컴퓨터 과학은 인디렉션 계층을 한 단계 더 만들면 모든 문제를 풀 수 있다고 믿는 학문이다.
-Dennis DeBruler

프로그래밍 초창기에는 서브루틴을 호출 할 때 오버헤드가 있어서 짧은 메소드 사용을 꺼려 했다고 한다. 그러나 요즘엔 프로세스 내부 호출(in-process call)로 인런 오버헤드가 상당부분 제거됐다고 한다. 하지만 서브루틴이 무엇인지 보려면 일단 그 부분으로 가보야 하기 때문에 사람들에게는 여전히 오버헤드가 존재한다. 그 오버헤드를 줄이기 위해서는 메소드의 이름을 잘 지어야 한다.

부분의 경우 메소드의 길이를 줄이기 위해서는 Extract Method를 사용한다. 메소드에 파라미터와 임시변수가 많으면 메소드를 추출하기가 어렵다.
  • 임시변수를 줄이기 위해서는 Replace Temp with Query를 사용할 수 있고
  • 긴 파라미터 리스트는 Introduce Parameter Object와 Preserve Whole Object로 짧게 할 수 있다.
  • 그럼에도 여전히 임시변수와 파라미터가 많다면 Replace Method with Method Object를 사용한다.
뽑아낼 코드 덩어리 식별 방법으로는 주석을 찾는 것이 좋다. 그리고 조건문과 루프 또한 추출이 필요하다는 신호를 준다. 조건식을 다루지 위해서는 Decompose Conditional을 사용한다. 루프의 경우는 Extract Method에서 예제로 다루고 있다.

Posted by 모과이IT
,