반응형
리팩토링 中
3. 코드속의 나쁜냄새 ( 냄새표를 참조하라 , ()안의 숫자는 참조 페이지번호 )
리팩토링을 해야하는 시점
인간의 직관. 코드속의 나쁜냄새를 직관으로 처리
3.1. 중복된 코드(uplicated Code)
악취퍼레이드의 일등
- Extract Method(136) : 한곳 이상에서 중복된 코드가 나타날때
- Pull Up Method(370) :동일한 슈퍼클래스를 갖는 두 서브클래스에서 같은 코드가 나타나는경우
- Extract Method(136), Form Template method(393) :코드가 비슷하기는 하지만 같지는 않다면,
- Substitute Algorithm(167) : 메소드들이 같은 작업을 하지만 다른 알고리즘을 사용한다면 두 알고리즘중 더 명확한 것을 선택
- Extract Class(179) : 서로 관계가 없는 두 클래스에서 중복된 코드가 있는경우 한쪽 클래스에서 를 사용한 다음 양쪽에서 이 새로운 클래스를 사용하도록 하는것을 고려
3.2 긴메소드(Long Method)
짧은 메소드로 된 코드를 이해하기 쉽게 하려면 무엇보다도 이름을 잘 지어야하며, 핵심은 메소드의 길이가 아니라 메소드가 하는 일과 일을 처리하는 방법 사이의 의미적거리(Semantic distance)이다.
- 대부분의 경우 메소드의 길이를줄이기 위해 하는것은 Extract Method(136) 이다.
- 메소드에 파리미터와 임시변수가 많다면, 메소드를 추출하기 어렵다. 임시변수를 제거하기 위해서는 Replace Temp wth Query(147)을 사용
- 긴 파라미터 리스트는 Introduce Parameter Object(339 : 파라미터를 객체(파라미터그룹)로전달), Preserve Whole Object(331: 어떤객체에서 여러값을 얻어서 파라미터로 던진다면 그 객체를 던져라)로 짧게 할수 있다.
조건문과 루프 또한 메소드 추출이 필요
- 조건을 다루기 위해서 Decompose Conditional(276 : 조건,then,else부분에서 메소드추출) 사용
- 루프의 경우는 루프와 그 안의 코드를 추출하여 하나의 메소드로 만든다.
3.3 거대한 클래스(Large Class)
지나치게많은 인스턴스변수가 나타날때 많은 변수를 묶기 위해 Extract Class(179)를 사용할 수 있다.
클래스내에서 서로에게 의미가 있는 변수를 골라서 묶는다. 클래스안에서 변수들의 어떤 부분집합에 대한 공통적인 접두사,접미사가 있으면 클래스를 따로 뽑아낸다.
만약 새로만들 클래스가 서브클래스로서 의미가 있으면 종종 Extract Subclass(278) 이 더쉬움
- 코드가 많은 클래스는 Extract Class, Extract Subclass
- 클라이언트가 클래스를 어떻게 쓰게 할것인지를 결정하고 각각의 사용방법에 대해 Extract Interface를 사용--> 클래스를 어떻게 분해할지에 대한 아이디어를 줄것이다.
- 큰클래스가 GUI클래스라면 Duplicate Observed Data(224)
3.4 긴 파라미터 리스트(Long Parameter List)
이미 알고있는 객체에 요청하여 파라미터의 데이타를 얻을 수 있으면 Replace Parameter with Method(335: 파라미터를 제거하고 수신자가 그 메소드를 호출, 수신자안에 계산과정을 추가)를 사용.
한 객체로부터 주워모은 데이타뭉치를객체 자체로 바꾸기위해 Preserve Whole Object(331) 사용
호출하는 객체와 큰객체사이에 종속성을 두고싶지 아니할때는 파라미터로 넘겨라. 고통이 뒤따를것이다.
3.5 확산적 변경(Divergent Change)
한클래스가 다른이유로 인해 다른방법으로 자주 변경되는 경우에 발생 --> 두개의 클래스로 나누어 하나의 클래스만 수정
Extract Class를 사용하여 하나의 클래스로 묶음
3.6 산탄총 수술(Shotgun Surgery)
확산적 변경과 비슷하지만 정반대.
변경을 할때마다 많은 클래스를 수정하는경우
- move Method(170), Move Filed(175) 을 사용하여 변경해야할부분을 모두 하나의 클래스로 몰아놓고 싶을 것이다.
- 종종 Inline Class(184)를 사용하여 모든동작을 하나로 모을수도 있다.
확산적변경은 여러종류의 변경때문에 하나의 클래스가 시달리는것이고 산탄총 수술을 하나를 변경햇을때 많은 클래스를 고쳐야하는경우이다.
3.7 기능에 대한 욕심 (Feature Envy)
어떤값을 계산하기 위해 다른 객체에 잇는 여러개의 get 메소드를 호출하는 경우
- 그 메소드는 분명히 다른곳에 있고 싶은것이고 따라서 Move Method(170 메소드 이동)을 사용한다.
- 때로는 메소드의 특정부분만 이런 욕심으로 고통받는데 이럴때는 욕심이 많은 부분에 대해서 Extract Method(136 메소드로 추출)을 사용한담음 Move Method 를 사용
3.8 데이타 덩어리(Data Clump)
함께 몰려다니는 데이터의 무리는 그들 자신의 객체로 만들어져야한다.
- 이 덩어리를 객체로 바꾸기 위해 이 필드들에 대해 Extract CLass(179)를 사용한다.
- 그런다음 관심을 메소드 시그너처로 돌려 Introduce parameter Object(339: 파라미터를 객체로 전환)나 Preserve Whole Obejct(331 파라미터값을 구하는 객체자체를 마라미터로 던진다)을 사용하여 파라미터 리스트를 단순하게 한다.
3.9 기본 타입에 대한 강박관념(primitive Obsession)
기본타입과 레코드타입을 객체로 바꿔라.
- 각각의 데이타 값에 대해 Replace Data Value with Object(209 : 추가적인 데이타나 동작을 필요로 하는 데이타아이템이 있을경우 데이타를 객체로 바꿔라)를 사용
- 데이터 값이 타입 코드이고, 값이 동작에 영향을 미치지 않는다면 Replace Type Code with Class(255 : 클래스의 동작에 영향을 미치지 않는 숫자로 된 타입코드가 있으면 숫자- 를 클래스로 바꾸어라)를 사용
- 만약 타입타입코드에 의존하는 조건문이 있는 경우에는 Replace Type Code With Subclass , Replace Type Code With State/Strategy(265)를 이용
- 항상 몰려다녀야 할 필드 그룹이 있따면 Extract Class(179)
- 파라미터 리스트에서 이런 기본타입을 보면 Introduce Parameter Object(339)를 사용하라.
- 배열을 쪼개서 쓰고있는 자신을 발견하거든 Replace Array with Object(220)을 사용하라.
3.10 Switch문(Switch Statements)
siwtch 문의 본질적인 특징은 중복된다는점
항상 다형성을 생각.
- Extract Method(136)을 사용하여 switch문을 뽑아내고, Move Method(170)를 사용하여 다형성이 필요한 클래스로 옮긴다
- 이시점에서 Replace Type Code with Subclasses(261)를 사용할것인지, Replace Type Code with State/Strategy(265)를 사용할것인지 결정해야한다.
- 상속구조를 결정했으면 Replace Conditional with Polymorphism(293)을 사용할수 있다.
- 만약 하나의 메소드에만 영향을 미치는 몇개의 경우가 있따면 굳이 바꿀 필요가 없다. Replace Parameter with Explict Methods(327)
- 조건중 null 이 있는경우 Introduce Null Object(298)
3.11 평행 상속 구조(Parallel Inheritance Hierarchies)
산탄총 수술의 특별한 경우
한 클래스의 서브클래스를 만들면 다른곳에도 모두 서브클래스를 만들어주어야하는경우
중복을 제거하는 일반적인 전략은 한쪽 상속구조의 인스턴스가 다른쪽 구조의 인스턴스를 참조하도록 만드는것
- Move Method(170), Move Filed(175)를 사용
3.12 게으른 클래스(Lazy Class)
충분한 일을 하지 않는 클래스는 삭제되어야한다.
- 별로 하는일도 없는 클래스의 서브클래스는 Collapse Hierarchy(392), 거의 필요없는 클래스는 Inline Class(184)
3.13 추측성 일반화(Speculative Generality)
- 필요하지도 않은 것을 처리하기 위한 추상클래스가 있으면 Collapse Hierarchy(392)
- 불필요한 위임은 Inline Class(184)
- 메소드에 사용되지 않는 파라미터가 있다면 Remove Parameter(318)
- 메소드 이름이 이상하고 추상적일 때는 Rename Method(313)
3.14 임시필드(Temporary Field)
사용되지 않는 변수가 왜 있는지를 이해하려는것은 짜증나는일
임시필드는 복잡한 알고리즘이 여러변수를 필요로 할때 흔히 나타남.
- Extract Class, Intoduce Null Object
3.15 메시지 체인 (Message Chains)
클라이언트가 객체의 클래스 구조와 결합되어있을때, 객체가 객체에 물어보고.. 이 객체는 다른객체에.. 또 이객체는 다른 객체에..
- Hide Delegate(187)을 사용.
- 때로는 Extract Method, Move Method로 체인의 밑으로 밀어넣는다.
3.16 미들맨(Middel Man)
메소드의 태반이 다른 클래스로 위임을 하고 있다면 Remove Middle Man(191 클라이언트가 대리객체를 직접 사용)을 사용.
몇몇 메소드가 많은 일을 하지 않는다면 inline Method(144)를 사용. 추가동작 필요시 Replace Delegation with Inheritance(404) 사용
3.17 부적절한 친밀(Inappropriate Intimacy)
클래스가 지나치게 친밀하게 되어 서로 사적인 부분을 파고드느라 너무 많은 시간이 걸릴때.
- Move Method,Move Field를 사용하여 조각으로 나누고 친밀함을 줄인다.
- Change Bidirectional Association to Unidirectional(236)이 적용가능한지 살피고,
- 공통관심사가 있다면 Extract Class를 사용하여 공통된부분을 별도의 클래스로 만들어라.
- 또는 Hide Delegate(487)을 사용하여 다른 클래스가 중계하도록 하라.
3.18 다른 인터페이스를 가진 대체 클래스 (Alternatice Classes with Different Interface)
- 같은 작업을 하지만 다른 시그너쳐를 가지고 있는경우는 Rename Method를 사용
- 포로토콜이 같아질때까지 Move Method를 이용하여 동작을 이동시켜라.
- 너무 많은 코드를 옮겨야할경우 Extract Superclass(384)를 사용.
3.19 불완전한 라이브러리 클래스(Incomplete Library Class)
라이브러리 클래스를 수정하는것은 거의 불가능하다.
- 라이브러리 클래스가 가지고 있었으면 하는 메소드가 몇개 있다면, Introduce Foreign Method(194)를 사용하라.
- 별도의 동작이 잔뜩 있다면 Introduce Local Extension(196)이 필요하다.
3.20 데이터 클래스(Data Class)
멍청하게 데이타만 저장하고 거의 대부분 다른 클래스에 의해 조작된다.
- public 필드는 Encapsulate Field(224)적용
- Collection 필드는 Encapsulate Collection(244)
- 값이 변경되면 안되는 필드에 대해서는 Remove setting Method(344) 적용
- get/set 메소드가 다른 클래스에서 사용될때 Move Method, Extract Method사용하여 데이터 클래스로 옮긴다.
3.21 거부된 유산(Refused Bequest)
만약 서브클래스가 동작은 재사용하지만 수퍼클래스의 인터페이스를 지원하는 것은 원치 않는다면 거부된 유산의 냄새는 심각한 문제.
- 클래스구조를 손보는것보다는 Replace Inheritance with Deligation(401)을 적용
3.22 주석 (Comments)
주석을 써야 할 것 같은 생각이 들면, 먼저 코드를 리팩토링하여 주석이 불필요하도록 하라.
- 코드 블록이 무슨 작업을 하는지 설명하기 위하여 주석이 필요하다면 Extract method를 시도.
- 그래도 주석이 필요하다면 Rename Method 사용
- 만약 시스템의 필요한 상태에 대한 어떤 규칙을 설명할 필요가 있다면 Introduce Assertion(306)을 사용.
3. 코드속의 나쁜냄새 ( 냄새표를 참조하라 , ()안의 숫자는 참조 페이지번호 )
리팩토링을 해야하는 시점
인간의 직관. 코드속의 나쁜냄새를 직관으로 처리
3.1. 중복된 코드(uplicated Code)
악취퍼레이드의 일등
- Extract Method(136) : 한곳 이상에서 중복된 코드가 나타날때
- Pull Up Method(370) :동일한 슈퍼클래스를 갖는 두 서브클래스에서 같은 코드가 나타나는경우
- Extract Method(136), Form Template method(393) :코드가 비슷하기는 하지만 같지는 않다면,
- Substitute Algorithm(167) : 메소드들이 같은 작업을 하지만 다른 알고리즘을 사용한다면 두 알고리즘중 더 명확한 것을 선택
- Extract Class(179) : 서로 관계가 없는 두 클래스에서 중복된 코드가 있는경우 한쪽 클래스에서 를 사용한 다음 양쪽에서 이 새로운 클래스를 사용하도록 하는것을 고려
3.2 긴메소드(Long Method)
짧은 메소드로 된 코드를 이해하기 쉽게 하려면 무엇보다도 이름을 잘 지어야하며, 핵심은 메소드의 길이가 아니라 메소드가 하는 일과 일을 처리하는 방법 사이의 의미적거리(Semantic distance)이다.
- 대부분의 경우 메소드의 길이를줄이기 위해 하는것은 Extract Method(136) 이다.
- 메소드에 파리미터와 임시변수가 많다면, 메소드를 추출하기 어렵다. 임시변수를 제거하기 위해서는 Replace Temp wth Query(147)을 사용
- 긴 파라미터 리스트는 Introduce Parameter Object(339 : 파라미터를 객체(파라미터그룹)로전달), Preserve Whole Object(331: 어떤객체에서 여러값을 얻어서 파라미터로 던진다면 그 객체를 던져라)로 짧게 할수 있다.
조건문과 루프 또한 메소드 추출이 필요
- 조건을 다루기 위해서 Decompose Conditional(276 : 조건,then,else부분에서 메소드추출) 사용
- 루프의 경우는 루프와 그 안의 코드를 추출하여 하나의 메소드로 만든다.
3.3 거대한 클래스(Large Class)
지나치게많은 인스턴스변수가 나타날때 많은 변수를 묶기 위해 Extract Class(179)를 사용할 수 있다.
클래스내에서 서로에게 의미가 있는 변수를 골라서 묶는다. 클래스안에서 변수들의 어떤 부분집합에 대한 공통적인 접두사,접미사가 있으면 클래스를 따로 뽑아낸다.
만약 새로만들 클래스가 서브클래스로서 의미가 있으면 종종 Extract Subclass(278) 이 더쉬움
- 코드가 많은 클래스는 Extract Class, Extract Subclass
- 클라이언트가 클래스를 어떻게 쓰게 할것인지를 결정하고 각각의 사용방법에 대해 Extract Interface를 사용--> 클래스를 어떻게 분해할지에 대한 아이디어를 줄것이다.
- 큰클래스가 GUI클래스라면 Duplicate Observed Data(224)
3.4 긴 파라미터 리스트(Long Parameter List)
이미 알고있는 객체에 요청하여 파라미터의 데이타를 얻을 수 있으면 Replace Parameter with Method(335: 파라미터를 제거하고 수신자가 그 메소드를 호출, 수신자안에 계산과정을 추가)를 사용.
한 객체로부터 주워모은 데이타뭉치를객체 자체로 바꾸기위해 Preserve Whole Object(331) 사용
호출하는 객체와 큰객체사이에 종속성을 두고싶지 아니할때는 파라미터로 넘겨라. 고통이 뒤따를것이다.
3.5 확산적 변경(Divergent Change)
한클래스가 다른이유로 인해 다른방법으로 자주 변경되는 경우에 발생 --> 두개의 클래스로 나누어 하나의 클래스만 수정
Extract Class를 사용하여 하나의 클래스로 묶음
3.6 산탄총 수술(Shotgun Surgery)
확산적 변경과 비슷하지만 정반대.
변경을 할때마다 많은 클래스를 수정하는경우
- move Method(170), Move Filed(175) 을 사용하여 변경해야할부분을 모두 하나의 클래스로 몰아놓고 싶을 것이다.
- 종종 Inline Class(184)를 사용하여 모든동작을 하나로 모을수도 있다.
확산적변경은 여러종류의 변경때문에 하나의 클래스가 시달리는것이고 산탄총 수술을 하나를 변경햇을때 많은 클래스를 고쳐야하는경우이다.
3.7 기능에 대한 욕심 (Feature Envy)
어떤값을 계산하기 위해 다른 객체에 잇는 여러개의 get 메소드를 호출하는 경우
- 그 메소드는 분명히 다른곳에 있고 싶은것이고 따라서 Move Method(170 메소드 이동)을 사용한다.
- 때로는 메소드의 특정부분만 이런 욕심으로 고통받는데 이럴때는 욕심이 많은 부분에 대해서 Extract Method(136 메소드로 추출)을 사용한담음 Move Method 를 사용
3.8 데이타 덩어리(Data Clump)
함께 몰려다니는 데이터의 무리는 그들 자신의 객체로 만들어져야한다.
- 이 덩어리를 객체로 바꾸기 위해 이 필드들에 대해 Extract CLass(179)를 사용한다.
- 그런다음 관심을 메소드 시그너처로 돌려 Introduce parameter Object(339: 파라미터를 객체로 전환)나 Preserve Whole Obejct(331 파라미터값을 구하는 객체자체를 마라미터로 던진다)을 사용하여 파라미터 리스트를 단순하게 한다.
3.9 기본 타입에 대한 강박관념(primitive Obsession)
기본타입과 레코드타입을 객체로 바꿔라.
- 각각의 데이타 값에 대해 Replace Data Value with Object(209 : 추가적인 데이타나 동작을 필요로 하는 데이타아이템이 있을경우 데이타를 객체로 바꿔라)를 사용
- 데이터 값이 타입 코드이고, 값이 동작에 영향을 미치지 않는다면 Replace Type Code with Class(255 : 클래스의 동작에 영향을 미치지 않는 숫자로 된 타입코드가 있으면 숫자- 를 클래스로 바꾸어라)를 사용
- 만약 타입타입코드에 의존하는 조건문이 있는 경우에는 Replace Type Code With Subclass , Replace Type Code With State/Strategy(265)를 이용
- 항상 몰려다녀야 할 필드 그룹이 있따면 Extract Class(179)
- 파라미터 리스트에서 이런 기본타입을 보면 Introduce Parameter Object(339)를 사용하라.
- 배열을 쪼개서 쓰고있는 자신을 발견하거든 Replace Array with Object(220)을 사용하라.
3.10 Switch문(Switch Statements)
siwtch 문의 본질적인 특징은 중복된다는점
항상 다형성을 생각.
- Extract Method(136)을 사용하여 switch문을 뽑아내고, Move Method(170)를 사용하여 다형성이 필요한 클래스로 옮긴다
- 이시점에서 Replace Type Code with Subclasses(261)를 사용할것인지, Replace Type Code with State/Strategy(265)를 사용할것인지 결정해야한다.
- 상속구조를 결정했으면 Replace Conditional with Polymorphism(293)을 사용할수 있다.
- 만약 하나의 메소드에만 영향을 미치는 몇개의 경우가 있따면 굳이 바꿀 필요가 없다. Replace Parameter with Explict Methods(327)
- 조건중 null 이 있는경우 Introduce Null Object(298)
3.11 평행 상속 구조(Parallel Inheritance Hierarchies)
산탄총 수술의 특별한 경우
한 클래스의 서브클래스를 만들면 다른곳에도 모두 서브클래스를 만들어주어야하는경우
중복을 제거하는 일반적인 전략은 한쪽 상속구조의 인스턴스가 다른쪽 구조의 인스턴스를 참조하도록 만드는것
- Move Method(170), Move Filed(175)를 사용
3.12 게으른 클래스(Lazy Class)
충분한 일을 하지 않는 클래스는 삭제되어야한다.
- 별로 하는일도 없는 클래스의 서브클래스는 Collapse Hierarchy(392), 거의 필요없는 클래스는 Inline Class(184)
3.13 추측성 일반화(Speculative Generality)
- 필요하지도 않은 것을 처리하기 위한 추상클래스가 있으면 Collapse Hierarchy(392)
- 불필요한 위임은 Inline Class(184)
- 메소드에 사용되지 않는 파라미터가 있다면 Remove Parameter(318)
- 메소드 이름이 이상하고 추상적일 때는 Rename Method(313)
3.14 임시필드(Temporary Field)
사용되지 않는 변수가 왜 있는지를 이해하려는것은 짜증나는일
임시필드는 복잡한 알고리즘이 여러변수를 필요로 할때 흔히 나타남.
- Extract Class, Intoduce Null Object
3.15 메시지 체인 (Message Chains)
클라이언트가 객체의 클래스 구조와 결합되어있을때, 객체가 객체에 물어보고.. 이 객체는 다른객체에.. 또 이객체는 다른 객체에..
- Hide Delegate(187)을 사용.
- 때로는 Extract Method, Move Method로 체인의 밑으로 밀어넣는다.
3.16 미들맨(Middel Man)
메소드의 태반이 다른 클래스로 위임을 하고 있다면 Remove Middle Man(191 클라이언트가 대리객체를 직접 사용)을 사용.
몇몇 메소드가 많은 일을 하지 않는다면 inline Method(144)를 사용. 추가동작 필요시 Replace Delegation with Inheritance(404) 사용
3.17 부적절한 친밀(Inappropriate Intimacy)
클래스가 지나치게 친밀하게 되어 서로 사적인 부분을 파고드느라 너무 많은 시간이 걸릴때.
- Move Method,Move Field를 사용하여 조각으로 나누고 친밀함을 줄인다.
- Change Bidirectional Association to Unidirectional(236)이 적용가능한지 살피고,
- 공통관심사가 있다면 Extract Class를 사용하여 공통된부분을 별도의 클래스로 만들어라.
- 또는 Hide Delegate(487)을 사용하여 다른 클래스가 중계하도록 하라.
3.18 다른 인터페이스를 가진 대체 클래스 (Alternatice Classes with Different Interface)
- 같은 작업을 하지만 다른 시그너쳐를 가지고 있는경우는 Rename Method를 사용
- 포로토콜이 같아질때까지 Move Method를 이용하여 동작을 이동시켜라.
- 너무 많은 코드를 옮겨야할경우 Extract Superclass(384)를 사용.
3.19 불완전한 라이브러리 클래스(Incomplete Library Class)
라이브러리 클래스를 수정하는것은 거의 불가능하다.
- 라이브러리 클래스가 가지고 있었으면 하는 메소드가 몇개 있다면, Introduce Foreign Method(194)를 사용하라.
- 별도의 동작이 잔뜩 있다면 Introduce Local Extension(196)이 필요하다.
3.20 데이터 클래스(Data Class)
멍청하게 데이타만 저장하고 거의 대부분 다른 클래스에 의해 조작된다.
- public 필드는 Encapsulate Field(224)적용
- Collection 필드는 Encapsulate Collection(244)
- 값이 변경되면 안되는 필드에 대해서는 Remove setting Method(344) 적용
- get/set 메소드가 다른 클래스에서 사용될때 Move Method, Extract Method사용하여 데이터 클래스로 옮긴다.
3.21 거부된 유산(Refused Bequest)
만약 서브클래스가 동작은 재사용하지만 수퍼클래스의 인터페이스를 지원하는 것은 원치 않는다면 거부된 유산의 냄새는 심각한 문제.
- 클래스구조를 손보는것보다는 Replace Inheritance with Deligation(401)을 적용
3.22 주석 (Comments)
주석을 써야 할 것 같은 생각이 들면, 먼저 코드를 리팩토링하여 주석이 불필요하도록 하라.
- 코드 블록이 무슨 작업을 하는지 설명하기 위하여 주석이 필요하다면 Extract method를 시도.
- 그래도 주석이 필요하다면 Rename Method 사용
- 만약 시스템의 필요한 상태에 대한 어떤 규칙을 설명할 필요가 있다면 Introduce Assertion(306)을 사용.
반응형
'Know > Java' 카테고리의 다른 글
유니코드(Unicode)와 유니코드 인코딩 (0) | 2005.08.08 |
---|---|
올바른 프레임워크의 선택과 사용법 (0) | 2005.07.15 |
Triple DES Encryption (DESede) (2) | 2005.03.25 |
ExclusiveOR 연산자의 번외게임 (0) | 2005.03.17 |
디자인패턴 예제 및 데모사이트 (0) | 2005.02.22 |