»
S
I
D
E
B
A
R
«
중복 제거: God Method
November 25th, 2009 by Wegra Lee
1.1. Problems & Constraints복수의 유사 목적 함수가 존재하고 구현 로직이 거의 동일하다. 극히 일부만 다른 코드가 여러 함수에 중복되어 있어, 향후 로직/결함 수정 시 human error 를 유발시키기 쉽다.입력 인자 중 일부가 서로 베타적이어서 하나로 합쳐서 제공하거나, 하나의 함수에서 다른 함수를 직접적으로 호출할 수 없다.Public API 가 이미 고정되어 있어, 서로 다른 인자들을 묶는 공통 타입을 만들고 함수들을 하나로 통합시키는 방법을 사용할 수 없다.혹은, 공통 타입이 존재하나 그 중 일부에 대해선 아무런 지원 계획이 없어, 이를 명확히 알리기 위해 복수의 독립적인 API 를 제공하고 싶다.1.2. Solution하나의 God Method(모든 상황에 필요한 인자의 합집합 받으며, 입력 조합에 반응하여 동작함)을 ‘내부’에 두고, 공개 메서드에서는 인자를 적절히 조합하여 이 God Method 를 호출한다.1.3. CautionsGod Method 는 하나의 기능에만 특화된 메서드에 비해 로직이 복잡하므로, 일반적인 경우에는 지양해야할 어프로치다.’중복량 vs. 복잡도 증가’ 를 잘 판단하여 적용 여부를 결정해야 하며, 적용하더라도 내부(private) 메서드에 한정 것이 좋다.1.4. Example아래의 세 함수들은 입력 인자 중 하나만이 다르며, 구현에서도 총 70라인 중 단 한 라인(CreateHttpRequestN 호출부)만이 다르다.DoSomething(..){       ..requestString = CreateRequestString(.., null, null,  ..);..}DoSomething(Circle circle, ..){       ..requestString = CreateRequestString(.., null, circle,  ..);..}DoSomething(Rectangle rectangle, ..){       ..requestString = CreateRequestString(.., rectangle, null,  ..);..}CreateHttpRequestN 함수에 필요한 모든 인자를 받아들이니 God Method 를 만들어 구현을 하나로 모은다.DoSomething_God // 새로 추가된 God Method(       Rectangle rectangle, Circle circle, ..){       ..requestString = CreateRequestString(.., rectangle, circle,  ..); // 받은 인자를 bypass..}DoSomething(..){       // 전후 중복 라인 사라짐return DoSomething_God(null, null,  ..); // God Method 호출}DoSomething(Circle circle, ..) const{return DoSomething_God(null, circle,  ..);}DoSomething(Rectangle rectangle, ..) const{return DoSomething_God(rectangle, null,  ..);}God Method 를 자세히 보면, public API 와 달리, Rectangle 와 Circle 인자를 포인터로 받고 있다. 이는 Rectangle, Circle 중 아무것도 사용하지 않는 메서드를 포용하기 위해서이다.
Problems & Constraints
복수의 유사 목적 함수가 존재하고 구현 로직이 거의 동일하다. 극히 일부만 다른 코드가 여러 함수에 중복되어 있어, 향후 로직/결함 수정 시 human error 를 유발시키기 쉽다.
입력 인자 중 일부가 서로 베타적이어서 하나로 합쳐서 제공하거나, 하나의 함수에서 다른 함수를 직접적으로 호출할 수 없다.
Public API 가 이미 고정되어 있어, 서로 다른 인자들을 묶는 공통 타입을 만들고 함수들을 하나로 통합시키는 방법을 사용할 수 없다.
혹은, 공통 타입이 존재하나 그 중 일부에 대해선 아무런 지원 계획이 없어, 이를 명확히 알리기 위해 복수의 독립적인 API 를 제공하고 싶다.
Solution
하나의 God Method(모든 상황에 필요한 인자의 합집합 받으며, 입력 조합에 반응하여 동작함)을 ‘내부’에 두고, 공개 메서드에서는 인자를 적절히 조합하여 이 God Method 를 호출한다.
Cautions
God Method 는 하나의 기능에만 특화된 메서드에 비해 로직이 복잡하므로, 일반적인 경우에는 지양해야할 어프로치다.
‘중복량 vs. 복잡도 증가’ 를 잘 판단하여 적용 여부를 결정해야 하며, 적용하더라도 내부(private) 메서드에 한정 짓는 것이 좋다.
Example
아래의 세 함수들은 입력 인자 중 하나만이 다르며, 구현에서도 총 70라인 중 단 한 라인(CreateHttpRequestN 호출부)만이 다르다.
DoSomething(..)
{       ..
requestString = CreateRequestString(.., null, null,  ..);
..
}
DoSomething(Circle circle, ..)
{       ..
requestString = CreateRequestString(.., null, circle,  ..);
..
}
DoSomething(Rectangle rectangle, ..)
{       ..
requestString = CreateRequestString(.., rectangle, null,  ..);
..
}
CreateHttpRequestN 함수에 필요한 모든 인자를 받아들이니 God Method 를 만들어 구현을 하나로 모은다.
DoSomething_God // 새로 추가된 God Method
(       Rectangle rectangle, Circle circle, ..)
{       ..
requestString = CreateRequestString(.., rectangle, circle,  ..); // 받은 인자를 bypass
..
}
DoSomething(..)
{       // 전후 중복 라인 사라짐
return DoSomething_God(null, null,  ..); // God Method 호출
}
DoSomething(Circle circle, ..) const
{
return DoSomething_God(null, circle,  ..);
}
DoSomething(Rectangle rectangle, ..) const
{
return DoSomething_God(rectangle, null,  ..);
}
God Method 를 자세히 보면, public API 와 달리, Rectangle 와 Circle 인자를 포인터로 받고 있다. 이는 Rectangle, Circle 중 아무것도 사용하지 않는 메서드를 포용하기 위해서이다.

지난달 포스팅한 Recommended Sequence for Refactoring[1] 의 가장 첫 단계인 Remove duplications 에 적용할 수 있는 리펙토링 기법이다.

곧 한 두개의 기법이 추가로 뒤따를 예정이다.

Problems & Constraints

  1. 복수의 유사 목적 함수가 존재하고 구현 로직이 거의 동일하다. 극히 일부만 다른 코드가 여러 함수에 중복되어 있어, 향후 로직/결함 수정 시 human error 를 유발시키기 쉽다.
  2. 입력 인자 중 일부가 서로 베타적이어서 하나로 합쳐서 제공하거나, 하나의 함수에서 다른 함수를 직접적으로 호출할 수 없다.
  3. Public API 가 이미 고정되어 있어, 서로 다른 인자들을 묶는 공통 타입을 만들고 함수들을 하나로 통합시키는 방법을 사용할 수 없다.
  4. 혹은, 공통 타입이 존재하나 그 중 일부에 대해선 아무런 지원 계획이 없어, 이를 명확히 알리기 위해 복수의 독립적인 API 를 제공하고 싶다.

Solution

하나의 God Method(모든 상황에 필요한 인자의 합집합 받으며, 입력 조합에 반응하여 동작함)을 ‘내부’에 두고, 공개 메서드에서는 인자를 적절히 조합하여 이 God Method 를 호출한다.

Cautions

God Method 는 하나의 기능에만 특화된 메서드에 비해 로직이 복잡하므로, 일반적인 경우에는 지양해야할 어프로치다.

‘중복량 vs. 복잡도 증가’ 를 잘 판단하여 적용 여부를 결정해야 하며, 적용하더라도 내부(private) 메서드에 한정 짓는 것이 좋다.

Examples

아래의 세 함수들은 입력 인자 중 하나만이 다르며, 그에 따라 달라지는 코드 라인수는 전체 코드 구현에서 비중이 작다. 혹은 중복 코드의 절대량이 많다.

public Object DoSomething(.. /* series of params */)
{  .. // tens of lines
MakeSomething(.., null, null,  ..);
.. // tens of lines
}

public Object DoSomething(Apple apple, .. /* series of params */)
{  .. // tens of lines
MakeSomething(.., apple, null,  ..);
.. // tens of lines
}

public Object DoSomething(Orange orange, .. /* series of params */)
{   .. // tens of lines
MakeSomething(.., null, orange,  ..);
.. // tens of lines
}

DoSomething 함수에 필요한 모든 인자를 받아들이니 God Method (DoAnything) 를 만들어 구현을 하나로 모은다.

private Object DoAnything(Apple apple, Orange orange, ..) // newly introduced god method
{  .. // tens of lines
MakeSomething(.., apple, orange, ..); // bypass the given params
.. // tens of lines
}

public Object DoSomething(..)
{  // no more duplicated lines
return DoAnything(null, null,  ..); // invoke the god method
}

public Object DoSomething(Apple apple, ..)
{
return DoAnything(apple, null,  ..);
}

public Object DoSomething(Orange orange, ..)
{
return DoAnything(null, orange,  ..);
}


References

  1. 권장 리팩터링 순서 (wegra.org)
  2. 중복 제거: Convert & Redirect (wegra.org)

»  Substance: WordPress   »  Style: Ahren Ahimsa