시작하며

Refactoring은 1999년의 Martin Fowler의 책[1]에서 소개 된 후, 계속 이야기 되고 있는 개념이다. Design Pattern과 같이 하나 하나 필요한 사례를 익히는 것도 시간이 걸리고 이를 실제로 활용하는데도는 더 많은 시간이 필요하다. 크게는 간단한 정의 및 Bad Smell에 대해서 살펴 본다. 실제 Technique은 옆에 두고 참고하기 위해서 따로 정리하였다.


정의

Refactoring은 Martin Fowler[1]가 제안한 코드 품질 향상 혹은 재설계의 방법이라고 할수 있다. Refactor라는 단의의 정의는 명사 및 동사의 의미를 가진다고 한다.


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

동사: to reconstruct software by applying a series of refactoring without changing its observable behavior


즉, 존재하는 코드를 기능의 변화 없이 안전하게 디자인을 향상 시키는 기법이다. 기능을 추가하면서도 Refactoring을 할 수 있기는 한다. 이는 구조가 변화 해야 하므로 기능을 변화 시키지 않고 변형을 하여 테스트를 하여 문제가 없다는 것을 확인한 후에 거기에 기능을 추가하는 방법이 적절하다.


어떻게 수행하는 하는가? 우선 Band Smell을 찾는다. 이것이 Refactor해야 하는 부분으로 인지하고 Refactoring을 적용하는 것이다.


[1]의 2nd Author인 Kent Beck의 Test Driven Development에서 Test를 수행하면서 개선하는 방법을 제안한다. 이것에 대한 이야기는 다음에 기회를 보아 살펴 봐야겠다.


Bad Smell

Refactoring을 해야 하는 시작점으로 볼 수 있다. 즉, 설계의 문제점이나 심각한 문제점의 조짐이라고 볼 수 있다. 이것들은 Martin Fowler 책에서 소개 된 것들이지만, 분류는 [2], [3]에 따른 것이다. (M)은 Martin Fowler의 책에 소개된 항목들이고 나머지들은 다른 항목들이다.



Bloaters

부풀어 오름이라고 번역이 가능할 것 같다. 즉, 일반적이거나 정상적이지 못하게 커지거나 길어진 상태라고 보면 되겠다.

. Long Method: 말 그대로 Method가 너무 길어진 형태이다. 이를 나누거나 줄이는 작업이 필요하다.

. Large Class (M): 큰 Class이다. Responsibility가 여러 개가 들어 있을 수도 있다. 또한 중복 코드가 포함되어 있을 수 있다.

. Primitive Obsession (M): Object를 사용하기 보다는 Primitive Data를 사용하는 경우가 있다. 이를 Object/Class로 만드는 것이다. Class를 추출("Extract Class")하거나 Parameter 객체를 도입("Introduce Parameter Object")하거나 어레이를 객체로 치환("Replace Array with Object") 할 수 있다.

. Long Parameter List (M): 매개변수(Parameter)가 간결한 것은 이해하는데 큰 장점이다. 만일 데이터가 객체로 부터 나오는 것이면 객체를 전달("Preserve Whole Object")를 수행하거나 Parameter 객체를 도입("Introduce Parameter Object")를 수행할 수 있다.

. Data Clumps (M): [1]의 한국어 버전에는 데이터 뭉치로 번역이 되어 있다. Class 내에서 3~4개 이상의 데이터 항목이 뭉쳐 다니는 경우가 있다. 이런 경우는 Class 추출("Extract Class")을 수행하거나 Parameter 객체 도입("Introduce Parameter Object")를 수행할 수 있다.


Object-Oriented Abusers

Object-Oriented 개념이 잘 적용되지 못한 경우들이다.

. Switch Statements (M): switch문이 꼭 나쁜 것은 아니지만, Object-Oriented에서 확연히 줄어드는 것이 특징이다. switch문은 코드 중복을 만들기 쉬우므로 이를 Polymorphism등으로 제거하는 방법을 고려할 수 있다. 또한 긴 if 문도 코드 중복등이 있을 수 있다. Method 추출("Extract Method") 혹은 Method 이동 ("Move Method") 또는 복잡한 형태로 Subclass로 Type Code를 치환("Replace Type Code wit Subclass)하거나 State/Strategy로 Type Code를 치환("Replace Type Code with State/Strategy)하는 방법이 있을 수 있다.

. Temporary Field (M): 임시 변수로 중간 결과를 가지고 다른 동작이나 판단을 하는 코드의 경우이다. 이는 Method 추출(Extract Method)를 통해서 임시 변수를 대체 할 수 있다.

. Refused Bequest (M): 거부된 유산. Martin Fowler 책의 번역본에는 방치된 상속물이라고 번역되어 있다. 즉, Inheritance를 했지만, 실제로 Superclass의 Method들이 사용되지 않는 경우이다. 즉, 잘못된 계층 구조 때문일 수 있다. 이때는, Superclass의 변형을 고려하거나 Delegation 관계로 바꾸는(Replace Inheritance with Delegation)을 고려할 수 있다.

. Alternative Classes with Different Interfaces: 비슷한 역할의 Class이지만 제공하는 Interface가 다른 Class들인 경우이다. 같은 기능을 하는 Method들의 이름을 통일 시키고, 피요한 경우에는 Superclass를 추출해 내는 것이 필요하다.

Change Preventers

변화 방해 요인으로 볼 수 있다. 

. Divergent Change (M): 하나의 Class가 다양한 원인으로 인해 수정되는 경우이다. 이는 Single Responsibility Principle을 위배했을 가능성이 높다. 그러므로 Class를 분리("Extract Class")를 고려하는 것이 좋다. 

. Shotgun Surgery (M): Divergent Change와 유사해 보인다. 하지만, 반대의 개념으로 하나를 수정하려고 하는데 수정이 산발적으로 여러 곳에서 일어나는 경우이다. 이는 Method나 Member 변수의 위치가 적절하지 못하다고 볼 수 있다. Method를 이동("Move Method")하거나 Field를 이동("Move Field")를 수행할 수 있다.

. Parallel Inheritance Hierarchies (M): 한 Class의 Subclass를 만들 때 마다, 매번 다른 Class의 Subclass를 만들어야 하는 상황이다. Shotgun Surgery의 특수항 경우라고 볼 수 있다. Shotgun Surgery와 유사하게 Move Method와 Move Field를 활용한다.

Dispensables

말 그대로 불필요한 것들이다. 

. Comments (M): Method에 설명을 위한 코멘트가 많이 달려 있는 것이다. 코멘트 보다는 코드가 자실을 잘 설명하도록
Refactoring나는 것이 좋다. 코멘트는 코드의 원리 혹은 무엇을 해야 할지 확실하지 않은 경우에 표시하는 정도가 좋다.

. Duplicate Code: 중복된 코드는 공통 부분으로 추출하여 Method로 만드는 것이 좋다.

. Lazy Class (M): Lazy Class는 결국 사용되지 않는 Class이다 관리를 위해서라도 이런 Class는 삭제하는 것이 좋다.

. Data Class (M): Getter, Setter만 있는 Class를 가리킨다. Class가 독립적으로 동작하지 않을 경우가 있으니, 어떻게 분리하여 다른 Class로 배분할지 고민하는 것이 좋다.

. Dead Code: 한마디로 죽은 코드로 삭제하는 방향이 맞다.

. Speculative Generality (M): 이는 미래 지향 코드로 필요하다고 생각해서 미리 넣어 둔 코드들인데, 실제 동작하지 않는 경우이다. 이도 삭제하는 편이 좋다.

Couplers

Coupling에서 온 개념으로 Class간의 잘못된 Dependency에 의한 Bad Smell이라고 볼 수 있다.

. Feature Envy (M): Method가 자신이 포함된 Class의 Data보다 다른 Class의 데이터를 계속 접근하려 하는 것이다. 이렇다면, Method를 옮기는 것을 고려하는 것이 좋다.

. Inappropriate Intimacy (M): 이 것은 Class가 다른 Class의 Field와 Method를 사용하려고 하는 경우이다.

. Message Chains (M): a->b()->c()->d()와 같이 연속해서 참조하여 Method를 호출하는 경우이다. 이런 경우는 Refacotring을 통해서 한번에 호출 되도록 Method를 옮기는 등의 방법을 고민해야 한다.

. Middle Man (M): 어떤 Class가 다른 한 Class의 Delagation 역할만 하는 경우이다. 이 때, 삭제하는 것을 고민해 볼 필요가 있다. 이 때, OCP를 위해서 Interface로 변경하거나 확장에 큰 도움이 되지 않는다면 삭제하는 것도 고민할 수 있겠다.

. Incomplete Library Class (M): library에서 제공하는 method들이 완전하지 않은 경우이다. 이 때도, 원작자가 그 기능을 추가해주면 좋지만, 그렇지 않다면 어떻게 필요한 기능을 추가할지에 대한 Bad Smell이다.


Refactoring Techniques

실제 Refactoring Techniques들은 계속 참조 할 수 있도록 Quick Reference Sheet를 만들어 보았다. 다른 분들과도 나누기 위해서 첨부하여 둔다.



돌아 보며

서두에서 이야기 한 것과 같이 이 내용도 계속해서 살펴 봐야 하는 내용이다. 특히, Refactoring은 설계를 한 후에 설계에 대해서 개선하는 작업이기 때문에 점진적인 작업과 반복 작업에 도움이 된다. 늘 주변을 깨끗하게 하는 것은 도움이 된다.



References

[1] Martin Fowler, John Brant, William Opdyke, and Don Roberts, "Refactoring: Improving the Design of Existing Code," Addison Wesely
[2] Refacotoring Guru, https://refactoring.guru/
[3] Source Making, https://sourcemaking.com/




+ Recent posts