리팩토링
소프트웨어 공학에서 리팩토링(refactoring)은 주로 '결과의 변경 없이 코드의 구조를 재조정함'을 뜻한다. 주로 가독성을 높이고 유지보수를 편하게 한다. 버그를 없애거나 새로운 기능을 추가하는 행위는 아니다. 사용자가 보는 외부 화면은 그대로 두면서 내부 논리나 구조를 바꾸고 개선하는 유지보수 행위이다.
자잘한 메소드들을 보기좋게 모아놓는다거나..
복잡한 메소드를 이해하기쉽게 각 기능마다 여러 메소드들로 구현해서 모아놓는다거나..
중복된 코드들을 하나로 묶는다거나.. 등등.
자세한 내용은 여기 .. >> 마틴 파울러의 리팩토링
* 대청 출판사에서 나온 Refactoring 번역서에서 발췌한 내용입니다.
1. 코드 속의 나쁜 냄새
1.1 중복된 코드
한 곳 이상에서 중복된 코드 구조가 나타난다면, 그것을 합쳐서 프로그램을 개선한다.
1.2 긴 코드
메소드의 길이가 아니라 메소드가 하는 일과 일을 처리하는 방법 사이의 의미적 거리이다.
메소드를 분해하는데 더 공격적이어야 한다.
1.3 거대한 클래스
클래스 하나가 너무 많은 일을 하려 할 때는 보통 지나치게 많은 인스턴스 변수가 나타난다.
이럴 경우, 중복된 코드가 존재할 확률이 높다.
1.4 긴 파라미터 리스트
긴 파라미터 리스트는 이해하기 어렵고, 일관성이 없거나 사용하기 어려울 뿐만 아니라, 다른 데이터가 필요할 때마다 계속 고쳐야 하기 때문에 파라미터 리스트는 짧은 게 좋다.
1.4 확산적 변경
한 클래스가 다른 이유로 인해 다른 방법으로 자주 변경되는 경우 발생한다.
1.5 산탄총 수술
클래스 하나를 변경했을 때 다른 많은 클래스를 고쳐야 하는 경우이다.
1.6 기능에 대한 욕심
메소드가 자신이 속한 클래스보다 다른 클래스에 관심을 가지고 있는 경우이다.
주석을 써야 할 것 같은 생각이 들면, 먼저 코드를 리팩토링 하여 주석이 불필요하도록 한다.
2. 리팩토링 메소드
2.2.메소드 정리(Composing Method)
2.2.1.Extract Method
그룹으로 함께 묶을 수 있는 코드 조각이 있으면, 코드의 목적이 잘 드러나도록 메소드의 이름을 지어 별도의 메소드로 뽑아낸다. 지나치게 긴 메소드나, 또는 목적을 이해하기 위해서 주석이 필요한 코드를 보면 그 부분을 하나의 메소드로 뽑아낸다.
2.2.2.Inline Method
메소드 몸체가 메소드의 이름만큼이나 명확할 때는, 호출하는 곳에 메소드의 몸체를 넣고, 메소드를 삭제하라.
2.2.3.Inline Temp
간단한 수식의 결과 값을 가지는 임시변수가 있고, 그 임시변수가 다른 리팩토링을 하는데 방해가 된다면, 이 임시변수를 참조하는 부분을 모두 원래의 수식으로 바꿔라.
2.2.4.Replace Temp with Query
어떤 수식의 결과 값을 저장하기 위해서 임시변수를 사용하고 있다면, 수식을 뽑아내서 메소드로 만들고, 임시변수를 참조하는 곳을 찾아 모두 메소드 호출로 바꾼다. 새로 만든 메소드는 다른 메소드에서도 사용될 수 있다.
2.2.5.Introduce Explaining Variable
복잡한 수식이 있는 경우에는, 수식의 결과나 또는 일부에 자신의 목적을 잘 설명하는 이름으로 된 임시변수를 사용하라.
2.2.6.Split Temporary Variable
루프 안에 있는 변수나 collecting temporary variable도 아닌 임시 변수에 값을 여러 번 대입하는 경우에는, 각각의 대입에 대해서 따로따로 임시변수를 만들어라.
2.2.7.Remove Assignments to Parameters
파라미터에 값을 대입하는 코드가 있으면, 대신 임시변수를 사용하도록 하라.
2.2.8.Replace Method with Method Object
긴 메소드가 있는데, 지역변수 때문에 Extract Method를 적용 할 수 없는 경우에는, 메소드를 그 자신을 위한 객체로 바꿔서 모든 지역변수가 그 객체의 필드가 되도록 한다. 이렇게 하면 메소드를 같은 객체 안의 여러 메소드로 분해할 수 있다.
2.2.9.Substitute Algorithm
알고리즘을 보다 명확한 것으로 바꾸고 싶을 때는, 메소드의 몸체를 새로운 알고리즘으로 바꾼다.
2.3.객체간의 기능 이동
2.3.1.Move Method
메소드가 자신이 정의된 클래스보다 다른 클래스의 기능을 더 많이 사용하고 있다면, 이 메소드를 가장 많이 사용하고 있는 클래스에 비슷한 몸체를 가진 새로운 메소드를 만들어라. 그리고 이 전 메소드는 간단한 위임으로 바꾸거나 완전히 제거하라.
2.3.2.Move Field
필드가 자신이 정의된 클래스보다 다른 클래스에 의해서 더 많이 사용되고 있다면, 타깃 클래스에 새로운 필드를 만들고 기존 필드를 사용하고 있는 모든 부분을 변경하라.
2.3.3.Extract Class
두 개의 클래스가 해야 할 일을 하나의 클래스가 하고 있는 경우, 새로운 클래스를 만들어서 관련 있는 필드와 메소드를 예전 클래스에서 새로운 클래스로 옮겨라.
2.3.4.Inline Class
클래스가 하는 일이 많지 않은 경우에는, 그 클래스에 있는 모든 변수와 메소드를 다른 클래스로 옮기고 그 클래스를 제거하라. Extract Class의 반대이다.
2.3.5.Hide Delegate
클라이언트가 객체의 위임 클래스를 직접 호출하고 있는 경우, 서버에 메소드를 만들어서 Delegate를 숨겨라.
2.3.6.Remove Middle Man
클래스가 너무 간단한 위임을 너무 많이 하고 있는 경우에는, 클라이언트가 Delegate를 직접 호출하도록 하라.
2.3.7.Introduce Foreign Method
사용하고 있는 서버 클래스에 부가적인 메소드가 필요하지만 클래스를 수정할 수 없는 경우에는, 첫 번째 인자로 서버 클래스의 인스턴스를 받는 메소드를 클라이언트에 만들어라.
2.3.8.Introduce Local Extension
사용하고 있는 서버 클래스에 여러 개의 메소드를 추가할 필요가 있지만 서버 클래스를 수정할 수 없는 경우, 필요한 추가 메소드를 포함하는 새로운 클래스를 만들어라. 이 확장 클래스를 원래 클래스의 서브 클래스 또는 wrapper 클래스로 만들어라.
2.4.데이터 구성(Organizing Data)
2.4.1.Self Encapsulate Field
필드에 직접 접근하고 있는데 필드에 대한 결합이 이상해지면, 그 필드에 대한 get/set 메소드를 만들고 항상 이 메소드를 사용하여 필드에 접근하라.
2.4.2.Replace Data Value with Object
추가적인 데이터나 동작을 필요로 하는 데이터 아이템이 있을 때는, 데이터 아이템을 객체로 바꾸어라.
2.4.3.Change Value to Reference
동일한 인스턴스를 여러 개 가지고 있는 클래스가 있고 여러 개의 동일한 인스턴스를 하나의 객체로 바꾸고 싶으면, 그 객체를 참조 객체로 바꾸어라.
2.4.4.Change Reference to Value
작고, 불변성이고 관리하기가 어려운 참조 객체가 있는 경우, 그것을 객체로 바꾸어라.
2.4.5.Replace Array with Object
배열의 특정 요소가 다른 뜻을 가지고 있다면, 배열을 각각의 요소에 대한 필드를 가지는 객체로 바꿔라.
2.4.6.Duplicate Observed Data
GUI 컨트롤에서만 사용 가능한 도메인 데이터가 있고, 도메인 메소드에서 접근이 필요한 경우, 그 데이터를 도메인 객체로 복사하고, observer를 두어 두 데이터를 동기화하라.
2.4.7.Change Unidirectional Association to Bidirectional
각각 서로의 기능을 필요로 하는 클래스가 있는데, 링크가 한쪽 방향으로만 되어 있는 경우,
반대 방향으로 포인터를 추가하고, 수정자가 양쪽 세트를 모드 업데이트하게 변경하라.
2.4.8.Replace Magic Number with Symbolic Constant
특별한 의미를 가지는 숫자 literal이 있으면, 상수를 만들고, 의미를 잘 나타내도록 이름을 지은 다음, 숫자를 상수로 바꾸어라.
2.4.9.Encapsulate Field
Public 필드가 있는 경우, 그 필드를 private로 만들고, 접근자를 제공하라.
2.4.10.Encapsulate Collection
Collection을 리턴 하는 메소드가 있으면, 그 메소드가 읽기전용 뷰를 리턴 하도록 만들고, add/remove 메소드를 제공하라.
2.4.11.Replace Record with Data Class
전통적인 프로그래밍 환경에서의 레코드 구조에 대한 인터페이스가 필요한 경우, 그 레코드를 위한 데이터 객체를 만들어라.
2.4.12.Replace Type Code with Class
클래스의 동작에 영향을 미치지 않는 숫자로 된 타입 코드가 있으면 숫자를 클래스로 바꾸어라.
2.4.13.Replace Type Code with Subclasses
클래스의 동작에 영향을 미치는 변경 불가능한 타입 코드가 있다면, 타입 코드를 서브 클래스로 바꾸어라.
2.4.14.Replace Type Code with State/Strategy
클래스의 동작에 영향을 미치는 타입 코드가 있지만 서브클래싱을 할 수 없을 때는, 타입 코드를 State 객체로 바꾸어라.
2.4.15.Replace Subclass with Fields
상수 데이터를 리턴하는 메소드만 다른 서브클래스가 있다면, 그 메소드를 수퍼클래스의 필드로 바꾸고 서브클래스를 제거하라.
2.5.조건문의 단순화
2.5.1.Decompose Conditional
복잡한 조건문이 있는 경우, 조건 then 부분, 그리고 else 부분에서 메소드를 추출하라.
2.5.2.Consolidate Conditional Expression
같은 결과를 초래하는 일련의 조건 테스트가 있는 경우, 그것을 하나의 조건 식으로 결합하여 뽑아내라.
2.5.3.Consolidate Duplicate Conditional Fragments
동일한 코드 조작이 조건문의 모든 분기 안에 있는 경우, 동일한 코드를 조건문 밖으로 옮겨라.
2.5.4.Remove Control Flag
일련의 boolean 식에서 컨트롤 플래그 역할을 하는 변수가 있는 경우, break 또는 return을 대신 사용하라.
2.5.5.Replace Nested Conditional with Guard Clauses
메소드가 정상적인 실행 경로를 불명확하게 하는 조건 동작을 가지고 있는 경우, 모든 특별한 경우에 대해서 보호절을 사용하라.
2.5.6.Replace Conditional with Polymorphism
객체의 타입에 따라 다른 동작을 선택하는 조건문을 가지고 있는 경우, 조건문의 각 부분을 서브 클래스에 있는 overriding 메소드로 옮겨라. 그리고 원래 메소드를 abstract로 만들어라.
2.5.7.Introduce Null Object
Null 체크를 반복적으로 하고 있다면, null 값을 null 객체로 대체하라.
2.5.8.Introduce Assertion
코드의 한 부분이 프로그램의 상태에 대하여 어떤 것을 가정하고 있으면, assertion을 써서 가정을 명시되게 만들어라.
2.6.메소드 호출의 단순화
2.6.1.Rename Method
메소드의 이름이 그 목적을 드러내지 못하고 있다면 메소드의 이름을 바꾸어라.
2.6.2.Add Parameter
어떤 메소드가 그 메소드를 호출하는 부분에서 더 많은 정보를 필요로 한다면, 이 정보를 넘길 수 있는 객체에 대한 파라미터를 추가하라.
2.6.3.Remove Parameter
파라미터가 메소드 몸체에서 더 이상 사용되지 않는다면, 그 파라미터를 제거하라.
2.6.4.Separate Query from Modifier
값을 리턴하는 경우뿐만 아니라 객체의 상태도 변경하는 메소드를 가지고 있는 경우, 두 개의 메소드를 만들어서 하나는 값을 리턴하는 역할을 하고, 하나는 객체의 상태를 리턴하는 역할을 하게 하라.
2.6.5.Parameterize Method
몇몇 메소드가 메소드 몸체에 다른 값을 포함하고 있는 것을 제외하고는 비슷한 일을 하고 있다면, 다른 값을 파라미터로 넘겨 받는 하나의 메소드를 만들어라.
2.6.6.Replace Parameter with Explicit Methods
파라미터의 값에 따라서 다른 코드를 실행하는 메소드가 있다면, 각각의 파라미터 값에 대한 별도의 메소드를 만들어라.
2.6.7.Preserve Whole Object
어떤 객체에서 여러 개의 값을 얻은 다음 메소드를 호출하면서 파라미터로 넘기고 있다면, 대신 그 객체를 파라미터로 넘겨라.
2.6.8.Replace Parameter with Method
객체가 메소드를 호출한 다음, 결과를 다른 메소드에 대한 파라미터로 넘기고 있다. 수신자 또한 이 메소드를 호출할 수 있다면, 그 파라미터를 제거하고 수신자가 그 메소드를 호출하도록 하라.
2.6.9.Introduce Parameter Object
자연스럽게 몰려다니는 파라미터 그룹을 가지고 있다면, 그것들을 객체로 바꾸어라.
2.6.10.Remove Setting Method
어떤 필드가 객체 생성시에 값이 정해지고 그 이후에는 변경되지 않아야 한다면, 그 필드 값을 설정하는 모든 메소드를 제거하라.
2.6.11.Hide Method
메소드가 다른 클래스에서 사용되지 않는다면, 그 메소드를 private로 만들어라.
2.6.12.Replace Constructor with Factory Method
객체를 생성할 때 단순히 생성하는 것 이외에 다른 작업도 하고 있다면, 생성자를 팩토리 메소드로 대체하라.
2.6.13.Encapsulate Downcast
메소드가 그 호출부에서 downcast될 필요가 있는 객체를 리턴하고 있다면, downcast하는 것을 메소드 안으로 옮겨라.
2.6.14.Replace Error Code with Exceptions
메소드가 에러를 나타내는 특별한 코드를 가지고 있다면, 대신 예외를 던져라.
2.6.15.Replace Exception with Test
호출부에서 먼저 검사할 수 있는 조건에 대해 예외를 던지고 있다면, 호출부가 먼저 검사하도록 바꿔라.
2.7.일반화 다루기
2.7.1.Pull Up Field
두 서브 클래스가 동일한 필드를 가지고 있다면, 그 필드를 수퍼클래스로 옮겨라.
2.7.2.Pull Up Method
동일한 일을 하는 메소드를 여러 서브클래스에서 가지고 있다면, 이 메소드를 수퍼클래스로 옮겨라.
2.7.3.Pull Up Constructor Body
서브클래스들이 대부분 동일한 몸체를 가진 생성자를 가지고 있다면, 수퍼클래스에 생성자를 만들고 서브클래스 메소드에서 이것을 호출하라.
2.7.4.Push Down Method
수퍼클래스에 있는 동작이 서브클래스 중 일부에만 관련되어 있다면, 그 동작을 관련된 서브클래스로 옮겨라.
2.7.5.Push Down Field
어떤 필드가 일부 서브클래스에 의해서만 사용되고 있다면, 그 필드를 관련된 서브클래스로 옮겨라.
2.7.6.Extract Subclass
어떤 클래스가 일부 인스턴스에 의해서만 사용되는 기능을 가지고 있다면, 기능의 부분집합을 담당하는 서브클래스를 만들어라.
2.7.7.Extract Superclass
비슷한 메소드와 필드를 가진 두 개의 클래스가 있다면, 수퍼 클래스를 만들어서 공통된 메소드와 필드를 수퍼 클래스로 옮겨라.
2.7.8.Extract Interface
여러 클라이언트가 한 클래스 인터페이스의 동일한 부분 집합을 사용하고 있거나, 두 클래스가 공통된 인터페이스를 가지는 부분이 있다면, 그 부분 집합을 인터페이스로 뽑아내라.
2.7.9.Collapse hierarchy
수퍼클래스와 서브클래스가 별로 다르지 않다면, 그것을 하나로 합쳐라.
2.7.10.From Template Method
각각의 서브클래스에, 동일한 순서로 비슷한 단계를 행하지만 단계가 완전히 같지는 않은 두 메소드가 있다면, 그 단계를 동일한 시그니처를 가진 메소드로 만들어라.
이렇게 하면 원래의 두 메소드는 서로 같아지므로, 수퍼클래스로 올릴 수 있다.
2.7.11.Replace Inheritance with Delegation
서브클래스가 수퍼클래스 인터페이스의 일부분만 사용하거나 또는 데이터를 상속 받고 싶지 않은 경우, 수퍼클래스를 위한 필드를 만들고 메소드들이 수퍼클래스에 위임하도록 변경한 후 상속 관계를 제거한다.
2.8.대규모 리팩토링
2.8.1.Tease Apart Inheritance
두 가지 작업을 한 번에 처리하는 상속 구조가 있는 경우, 두 개의 상속 구조를 만들고 하나가 다른 하나를 호출하도록 delegation을 사용하라.
2.8.2.Convert Procedural Design to Objects
절차적 스타일로 작성된 코드가 있다면, 데이터 레코드를 객체로 바꾸고, 동작을 쪼개서 옮겨라.
2.8.3.Separate domain from Presentation
도메인 로직을 포함하고 있는 GUI 클래스를 가지고 있다면, 도메인 로직을 분리하여 도메인 클래스를 만들어라.
2.8.4.Extract Hierarchy
너무 많은 작업을 하거나 또는 부분적으로라도 많은 조건문이 있는 클래스에 대해서는, 각각의 서브클래스가 특정 작업을 담당하도록 클래스의 상속 구조를 만들어라.
'Software Development' 카테고리의 다른 글
JAVA - FileInputStream FileReader 차이점. (0) | 2011.10.25 |
---|---|
VHDL - 예약어, 연산자 요약 (0) | 2011.03.15 |
VHDL - 개요와 역사 요약 (0) | 2011.03.15 |
Design Pattern(Java) - Iterator패턴 (0) | 2010.11.26 |
디자인 패턴의 이해 (0) | 2010.06.15 |