U E D R , A S I H C RSS

Refactoring/Bad Smells In Code

Refactoring이 동작하는 매커니즘을 아는 것 만큼 중요한 것은, 언제 Refactoring을 적용할까 하는 것이다.

여기서 딜레마가 온다. 어떻게 인스턴스 변수를 삭제하거나 클래스 계증구조를 만드는가를 표현하는 것은 쉽다. 그건 사소한 문제들이다. 하지만 언제 이러한 것들을 해야 할 것인지 표현하는 것은 쉽지 않다. 나는 (여기서의 I는 Martin Fowler) 프로그래밍 미학이라는 모호한 표현으로 얼버무리지 않고 좀 더 확실한 것을 원했다.

내가 이 문제로 Kent Beck 을 방문했을 때 그는 "언제" 를 설명하기 위해서 "Smell" 이라는 표현을 사용했다. 우리는 많은 코드들을 보았고, 그것들을 보면서 Refactoring이 적용가능한 어떤 구조를 발견했다.

여기에서 우리는 Refactoring이 적용가능한 아주 정확한 척도를 제공하려고는 하지 않을 것이다. 경험상, 어떠한 측정도구들도 숙련된 인간의 직관의 경쟁상대가 될 수는 없었다. 우리가 하려는 것은 Refactoring에 의해 해결될 수 있는 문제들이 있는 몇몇 부분을 지적하려는 것이다.

어떠한 Refactoring을 해야 할 지 확신할 수 없을때 이 부분을 읽어라. 정확하게 똑같은 Smell을 발견할 순 없더라도 Refactoring에 대한 올바른 방향을 가리켜 줄 지침이 될 것이다.

Duplicated Code

중복코드 코드
  • 같은 클래스내에 2개이상의 메소드들에 중복코드 존재시 - ExtractMethod
  • 두개 이상 서브클래스 내 중복코드시 - ExtractMethod 한뒤 PullUpField
  • 코드는 비슷하지만 똑같지는 않은 경우 - 비슷한 부분에 대해서 ExtractMethod
  • 다른 알고리즘 내에서 같은 일을 하는 메소드 - SubstituteAlgorithm
  • 두개이상의 연관없는 클래스 내의 중복코드 - ExtractClass

ExtractMethod, ExtractClass, PullUpMethod, FormTemplateMethod

Long Method

코드의 길이가 긴 메소드
  • 대부분의 경우에 대해서 - ExtractMethod
  • ExtractMethod 하는중 parameter를 많이 넘겨야 하거나, 임시변수를 많이 사용하게 되는 경우 - ReplaceTempWithQuery, IntroduceParameterObject, PreserveWholeObject, ReplaceMethodWithMethodObject
  • 조건 & 반복문 - DecomposeConditional

ExtractMethod, ReplaceTempWithQuery, ReplaceMethodWithMethodObject, DecomposeConditional

Large Class

너무 하는 일이 많은 큰 클래스
  • 수많은 변수들 - ExtractClass, ExtractSubclass
  • 꼭 항상 사용되지는 않는 인스턴스 변수들 - ExtractClass, ExtractSubclass
  • GUI 클래스에서 데이터부가 중복될때 - DuplicateObservedData
  • AWT -> Swing Component로 바꿀때 - DuplicateObservedData

ExtractClass, ExtractSubclass, ExtraceInterface, ReplaceDataValueWithObject

Long Parameter List

Parameter 인자가 많은 함수. 이해하기 힘들고, 사용하기 어렵다.
  • When you can get the data in one parameter by making a request of an object you already know about - ReplaceParameterWithMethod
  • to take a bunch of data gleaned from an object and replace it with the object itself - PreserveWholeObject
  • logic을 가지지 않는 여러개의 data item을 가지는 경우 - IntroduceParameterObject

ReplaceParameterWithMethod, IntroduceParameterObject, PreserveWholeObject

Divergent Change

하나의 클래스가 각각 다른 이유들로 인해서 다른 방식으로 자주 변경될 때.
다른 클래스들이 바뀔 때마다 매번 수정되는 부분.

ex)
  • 새 데이터베이스가 생길때마다 3개의 메소드를 바꿔야 한다.
  • 바뀌어야 하는 경우들을 명확하게 한뒤 ExtractClass 하여 하나의 클래스에 모은다.

ExtractClass

Shotgun Surgery

하나의 변화에 의해 다른 여러 클래스들의 변경 필요시
  • 바뀌는 부분들에 대해 MoveMethod, MoveField 하여 하나의 클래스에 넣는다. (없으면 새로 하나 클래스 생성할것)
  • 모든 행위들의 묶음을 가지기 위해 - InlineClass

    • Divergent Change - one class that suffers many kinds of changes
    • shotgun surgery - one change that alters many classes

MoveMethod, MoveField, InlineClass

Feature Envy

어떤 메서드가 자신이 속해 있는 클래스의 데이터가 아닌 다른 클래스의 데이터들을 필요로 할 때.
MoveMethod, MoveField, ExtractMethod

Data Clumps

클래스의 변수선언 필드나 함수 프로토타입 같은 곳에 있는, 항상 같이 몰려다니는 데이터 아이템들
  • ExtractClass, IntroduceParameterObject or PreserveWholeObject
  • 처음에 Data Clump들을 ExtractClass했을때 Field 들 묶음으로 보일 수도 있지만 너무 걱정할 필요는 없다.

ExtractClass, IntroduceParameterObject, PreserveWholeObject

Primitive Obsession

기본 데이터형 : Class 에 대해

ReplaceValueWithObject, ExtraceClass, IntroduceParameterObject, ReplaceArrayWithObject, ReplaceTypeCodeWithClass, ReplaceTypeCodeWithSubclasses, ReplaceTypeCodeWithState/Strategy

Switch Statements

길이가 긴 Switch-Case 문.
  • 대부분의 경우 - polymorphism으로 해결 가능
  • switch-case 부분을 ExtractMethod 한 뒤, polymorphism이 필요한 class에 MoveMethod 한다. 그리고 나서 ReplaceTypeCodeWithSubclassesReplaceTypeCodeWithState/Strategy 를 할 것을 결정한다. 상속구조를 정의할 수 있을때에는 ReplaceConditionalWithPolyMorphism 한다.
  • polymorphism을 이용하기에는 너무 작아 오히려 cost가 더 드는 경우 - ReplaceParameterWithExplicitmethods
  • 조건 case 에 null 이 있는 경우 - IntroduceNullObject

ReplaceConditionalWithPolymorphism, ReplaceTypeCodeWithSubclasses, ReplaceTypeCodeWithState/Strategy, ReplaceParameterWithExplicitMethods, IntroduceNullObject

Parallel Inheritance Hierarchies

병렬 상속 구조. shotgun surgery의 특별 케이스가 된다. -_-a

MoveMethod, MoveField

Lazy Class

거의 쓸모없는 클래스.

  • 별로 사용하지 않는 subclass들 - CollapseHierarchy
  • 거의 쓸모없는 컴포넌트들 - InlineClass

InlineClass, CollapseHierarchy

Speculative Generality

일어날 가능성이 거의 없는 일까지 다 대비한 필요없는 코드. 모든 기능들과 절차들은 제대로 이용되었을때 쓸모있다.
  • 추상클래스들이 별로 하는 일이 없을때 - CollapseHierarchy
  • 불필요한 Delegation - InlineClass
  • 사용하지 않는 parameter들을 가진 메소드 - RemoveParameter
  • 추상적인 두리뭉실한 메소드 이름 -_-; - RenameMethod 로 지상으로 내려오도록 하라는.. --;

CollapseHierarchy, InlineClass, RemoveParameter, RenameMethod

Temporary Field

특별한 상황에서만 세팅되는 변수를 가진 객체.

ExtractClass, IntroduceNullObject

Message Chains

객체를 부르고 그 객체가 다른 객체를 부르고, 그 다른 객체는 또 또 다른 객체를 부르고.. --

HideDelegate

Middle Man

delegation 의 남용.

RemoveMiddleMan, InlineMethod, ReplaceDelegationWithInheritance

Inappropriate Intimacy

사적인 부분(?)에 대해서 지나치게 관심을 기울이는 위험한(?) 클래스들.

MoveMethod, MoveField, ChangeBidirectionalAssociationsToUnidirectional, ReplaceInheritanceWithDelegation, HideDelegation

Alternative Classes with Different Interfaces

같은 일을 하지만 다른 signature를 가진 메서드들.

RenameMethod, MoveMethod

Incomplete Library Class

라이브러리에서 제공하는 메서드들이 불충분할 때.


IntroduceForeignMethod, IntroduceLocalExtension

Data Class

필드, getter, setter만을 가진 어린아이와 같은 클래스.

MoveMethod, EncapsulateField, EncapsulateCollection

Refused Bequest

서브클래스가 부모의 behavior는 재사용하나 부모의 인터페이스를 지원하기를 원하지는 않을 때.

ReplaceInheritanceWithDelegation

Comments

나쁜 냄새를 가리기 위한 방향제로 사용되는 주석. --;


주석을 쓸 필요가 있다는 느낌이 들때 일단 코드를 리펙토링 하면 주석을 많이 쓸 필요가 없음을 알게 된다.


주석을 이용할 좋은 시기는 도대체 무엇을 해야 할 지 모르겠을 때 이다. 무엇을 할 것인지 주석으로 먼저 서술함으로서 주석은 프로그래머가 무엇을 해야 할 지 확신할 수 없을 때 좋은 지침서가 된다. 주석은 ' 당신이 이것을 하는가' 를 말하기 위한 좋은 장소이다.

ExtractMethod, IntroduceAssertion


전에 JuNe 형이 최한기의 신기통을 언급하면서 Metaphor 로서 'Smell' 이 잘 맞아떨어짐을 이야기하던게 생각. '냄새란 일단 그 자체로 악취를 풍길 뿐만 아니라, 밖으로 점차적으로 퍼지고, 사람에게 배어들 수 있으며, 사람에게 배어들고 나면 그 사람이 냄새에 대해 인식을 하지 못한다.'. Smell 에 민감한 사람들은 작은 Refactoring 도 잘 해낼 수 있다. -- 1002


Refactoring
Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2009-05-27 07:09:19
Processing time 0.1094 sec