3. Snarled Method
• dominated by a single large, indented
section
____________________________
____________________
__________________________
___________________________
___________________
____________________________
______________________
___________________________
_______________________
__________________________
__________________
______________________________
_______________
4. 몬스터 함수 격파에 앞서
• refactoring tool이 있고 없고 차이 크다.
• extract method 같은 경우 사용 빈도가
크기 때문에 대부분의 리팩토링 툴이 지원
하는데,
• 툴이 안젂하게 extract method를 수행하
면 테스트 안 해도 된다.
5. Tackling Monsters with
Automated Refactoring Support
• key goals
– dependency가 작은 chunk부터 logic과 분리
시킨다.
– 이후 진행될 refactoring의 test를 쉽게 하기
위해 seam을 삽입한다.
• 리팩토링 툴을 사용하면 extract method
는 test 없이 진행 가능하다.
6. class CommoditySelectionPanel
{
…
public void update()
{
if (commodities.size() > 0
&& commodities.GetSource().equals(“local”))
{
listbox.clear();
for (Iterator it = commodities.iterator();
it.hasNext(); )
{
Commodity commodity = (Commodity)it.next();
if (commodity.isTwilight()
&& !commodity.match(broker))
{
listbox.add(commodity.getView());
}
}
}
}
}
7. class CommoditySelectionPanel 동작이 함수단위로 변경됐을 뿐
{
… 구조적으로 바뀐 건 하나도 없다.
public void update() 이후 Dependency를 제거하고 유닛 테스트를 진행한다.
{
if (commoditiesAreReadyForUpdate())
{
clearDisplay();
updateCommodities();
}
}
private boolean commoditiesAreReadyForUpdate()
{ return commodities.size() > 0 && commodities.GetSource().equals(“local”); }
private void clearDisplay() { listbox.clear(); }
private void updateCommodities()
{
for (Iterator it = commodities.iterator(); it.hasNext(); )
{
Commodity commodity = (Commodity)it.next();
if (singleBrokerCommodity(commodity))
{
displayCommodity(commodity.getView());
}
}
}
private boolean singleBrokerCommodity(Commodity commodity)
{ return commodity.isTwilight() && !commodity.match(broker); }
private void displayCommodity(CommodityView view)
{ listbox.add(commodity.getView()); }
}
8. The Manual Refactoring Challenge
• 유닛 테스트를 진행하면서 리팩토링.
• Extract method에서 실수하는 부분.
– 변수 젂달을 잊는 경우
– base class의 method와 동일한 이름을 짓는
경우
– 잘못된 type을 패러매터로 선언 혹은 리턴
9. Introduce Sensing Variable
public class DOMBuilder
{
…
void processNode(XDOMNSnippet root, List childNodes)
{
if (root != null)
{
if (childNodes != null)
root.addNode(new XDOMNSnippet(childNodes));
root.addChild(XDOMNSnippet.NullSnippet);
}
List paraList = new ArrayList();
XDOMNSnippet snippet = new XDOMNReSnippet();
snippet.setSource(m_state);
for (Iterator it = childNodes.iterator(); it.hasNext(); )
{
XDOMNNode node = (XDOMNNode)it.next();
if (node.type() == TF_G || node.type() == TF_H ||
(node.type() == TF_GLOT && node.isChild()))
{
paraList.addNode(node);
}
}
}
}
10. public class DOMBuilder
{
…
public boolean nodeAdded = false;
void processNode(XDOMNSnippet root, List childNodes)
{
if (root != null)
{
if (childNodes != null)
root.addNode(new XDOMNSnippet(childNodes));
root.addChild(XDOMNSnippet.NullSnippet);
}
List paraList = new ArrayList();
XDOMNSnippet snippet = new XDOMNReSnippet();
snippet.setSource(m_state);
for (Iterator it = childNodes.iterator(); it.hasNext(); )
{
XDOMNNode node = (XDOMNNode)it.next();
if (node.type() == TF_G || node.type() == TF_H ||
(node.type() == TF_GLOT && node.isChild()))
{
paraList.addNode(node);
nodeAdded = true;
}
}
}
}
11. Sensing Variable을 사용한 Unit Test
void testAddNodeOnBasicChild()
{
DOMBuilder builder = new DomBuilder();
List children = new ArrayList();
children.add(new XDOMNNode(XDOMNNode.TF_G));
builder.processNode(new XDOMNSnippet(), children);
assertTrue(builder.nodeAdded);
}
void testNoAddNodeOnBasicChild()
{
DOMBuilder builder = new DomBuilder();
List children = new ArrayList();
children.add(new XDOMNNode(XDOMNNode.TF_A));
builder.processNode(new XDOMNSnippet(), children);
assertFalse(builder.nodeAdded);
}
12. public class DOMBuilder
{ extract method를 한 후에 unit test를 통과하면
… sensing variables과 test code를 삭제한다.
public boolean nodeAdded = false;
void processNode(XDOMNSnippet root, List childNodes)
{
if (root != null)
{
if (childNodes != null)
root.addNode(new XDOMNSnippet(childNodes));
root.addChild(XDOMNSnippet.NullSnippet);
}
List paraList = new ArrayList();
XDOMNSnippet snippet = new XDOMNReSnippet();
snippet.setSource(m_state);
for (Iterator it = childNodes.iterator(); it.hasNext(); )
{
XDOMNNode node = (XDOMNNode)it.next();
if (isBasicChild(node))
{
paraList.addNode(node);
nodeAdded = true;
}
}
}
private boolean isBasicChild(XDOMNNode node)
{
return node.type() == TF_G || node.type() == TF_H || node.type() ==
TF_GLOT && node.isChild();
}
}
13. Break Out a Method Object
• Sensing variable 대신에 사용할 수 있다.
• 메서드의 parameter를 생성자의 parameter로
받고 run() 또는 execute()와 같은 메서드로 동
작한다.
• temporary variable을 instance variable로 변
경할 수 있다.
– sensing variable 같은 경우 기존에 있는 변수를 사용
하기에 적합하나 temporary 혹은 local 변수라서
test에 사용하기 어려운 경우가 있다.
– Method Object는 좋은 해결책이 된다.
14. Extract What You Know
• 작은 코드 조각을 테스트 없이 extract한
다.
– two or three lines
• 테스트와 다음 작업을 위한 좋은 출발점
• Coupling count가 0일때 사용.
– # of values that pass into and out
• Coupling count가 0보다 클때는 Sensing
variable을 사용해라.
15. Coupling count 예
void process(int a, int b, int c)
{
int maximum;
if (a > b) maximum = a;
else maximum = b;
}
void process(int a, int b, int c)
{
int maximum = max(a,b);
}
in : a, b
out : max return int
Coupling count : 3
16. Gleaning Dependencies
• 보호하고 지켜야 하는 logic에 대해서 test
를 작성한다.
• 테스트 범위에 들어가지 않는 chuck를 추
출한다.
• 적어도 중요한 동작이 제대로 돌아갂다는
확신을 할 수 있다.
17. void addEntry(Entry entry)
{
if (view != null && DISPLAY == true)
{
view.show(entry);
}
…
if (entry.category().equals(“single”) ||
entry.category(“dual”))
{
entries.add(entry);
view.showUpdate(entry, view.GREEN);
}
else
{
…
}
• Display code에 관한 실수가 있으면 빨리 알아챌 수 있다.
}
• 하지만 add login에서 에러가 있으면 찾는데 시갂이 걸릴 것이다.
• add login에 관한 테스트를 작성하자.
• 테스트가 성공하면 display code를 추출한다.
18. Sensing variables
• 테스트 하는데 사용해서 리팩토링을 돕는
다.
• sensing variables로 쓰기에 딱인데,
method의 local variable인 경우가 많다.
– instance variable인 경우에는 method가 동
작한 후에도 sense할 수 있다.
• 그래서 local variables into instance
variable로 고치는데 많이 혺동된다.
19. Strategy
• Skeletonize Methods
• Find Sequences
• Extract to the Current Class First
• Extract Small Pieces
• Be Prepared to Redo Extractions
20. Skeletonize Methods
if (marginalRate() > 2 && order.hasLimit())
{
order.readjust(rateCalculator.rateForToday());
order.recalculate();
}
if (orderNeedsRecalculation(order))
{
recalculateOrder(order, rateCalculator);
}
메서드만 남아있는 상태
23. • Extract to the Current Class First
– 현재 클래스에서 먼저 extract해라.
• Extract Small Pieces
– 작은 조각을 extract하는 것은 좋은 출발점.
• Be Prepared to Redo Extractions