Refactoring (나쁜 디자인의 코드를 좋은 디자인으로 바꾸는 방법)



Similar documents
gnu-lee-oop-kor-lec06-3-chap7

JAVA PROGRAMMING 실습 08.다형성

예제 2) Test.java class A intvar= 10; void method() class B extends A intvar= 20; 1"); void method() 2"); void method1() public class Test 3"); args) A

Microsoft PowerPoint - 2강

PowerPoint 프레젠테이션

PowerPoint Presentation

쉽게 풀어쓴 C 프로그래밍

C# Programming Guide - Types

Microsoft PowerPoint 자바-기본문법(Ch2).pptx

PowerPoint Presentation

PowerPoint 프레젠테이션

01-OOPConcepts(2).PDF

PowerPoint 프레젠테이션

q 이장에서다룰내용 1 객체지향프로그래밍의이해 2 객체지향언어 : 자바 2

PowerPoint 프레젠테이션

No Slide Title

유니티 변수-함수.key

<4D F736F F F696E74202D20C1A63038C0E520C5ACB7A1BDBABFCD20B0B4C3BC4928B0ADC0C729205BC8A3C8AF20B8F0B5E55D>

PowerPoint 프레젠테이션

JUNIT 실습및발표

09-interface.key

(8) getpi() 함수는정적함수이므로 main() 에서호출할수있다. (9) class Circle private double radius; static final double PI= ; // PI 이름으로 로초기화된정적상수 public

어댑터뷰

제11장 프로세스와 쓰레드

Microsoft PowerPoint - java1-lab5-ImageProcessorTestOOP.pptx

JAVA PROGRAMMING 실습 05. 객체의 활용

Design Issues

슬라이드 1

PowerPoint Presentation

Microsoft PowerPoint - chap05-제어문.pptx

PowerPoint Presentation

Contents. 1. PMD ㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍ 2. Metrics ㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍ 3. FindBugs ㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍ 4. ㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍ

PowerPoint 프레젠테이션

PowerPoint Presentation

05-class.key

5장.key

JAVA 프로그래밍실습 실습 1) 실습목표 - 메소드개념이해하기 - 매개변수이해하기 - 새메소드만들기 - Math 클래스의기존메소드이용하기 ( ) 문제 - 직사각형모양의땅이있다. 이땅의둘레, 면적과대각

PowerPoint Presentation

자바 프로그래밍

JAVA PROGRAMMING 실습 07. 상속

K&R2 Reference Manual 번역본

Java Programing Environment

(Microsoft PowerPoint - java1-lecture11.ppt [\310\243\310\257 \270\360\265\345])

쉽게 풀어쓴 C 프로그래밍

슬라이드 1

JAVA PROGRAMMING 실습 02. 표준 입출력

Microsoft PowerPoint - Lect04.pptx

교육자료

Microsoft PowerPoint - lec2.ppt

JMF3_심빈구.PDF

PowerPoint 프레젠테이션

PowerPoint Presentation

PowerPoint Presentation

rmi_박준용_final.PDF

JAVA PROGRAMMING 실습 09. 예외처리

Microsoft PowerPoint - 04-UDP Programming.ppt

(Microsoft PowerPoint - java2-lecture3.ppt [\310\243\310\257 \270\360\265\345])

쉽게 풀어쓴 C 프로그래밍

4장.문장

OOP 소개

소프트웨어공학개론 강의 5: 객체지향개념 최은만동국대학교컴퓨터공학과

Microsoft PowerPoint - 14주차 강의자료

Spring Data JPA Many To Many 양방향 관계 예제

신림프로그래머_클린코드.key

Microsoft PowerPoint - additional01.ppt [호환 모드]

<B0ADC8ADC7D0C6C428C3D6C1BE292E687770>

chap10.PDF

PowerPoint Presentation

PowerPoint 프레젠테이션

PowerPoint Presentation

歯 Final.PDF

PowerPoint Presentation

2힉년미술

Microsoft PowerPoint - Java7.pptx

Microsoft PowerPoint - C++ 5 .pptx

쉽게 풀어쓴 C 프로그래밊

C++ Programming

Microsoft PowerPoint - 3ÀÏ°_º¯¼ö¿Í »ó¼ö.ppt

제8장 자바 GUI 프로그래밍 II

JVM 메모리구조

ThisJava ..

Microsoft PowerPoint - chap04-연산자.pptx

프로그램을 학교 등지에서 조금이라도 배운 사람들을 위한 프로그래밍 노트 입니다. 저 역시 그 사람들 중 하나 입니다. 중고등학교 시절 학교 도서관, 새로 생긴 시립 도서관 등을 다니며 책을 보 고 정리하며 어느정도 독학으르 공부하긴 했지만, 자주 안하다 보면 금방 잊어

Microsoft PowerPoint - chap11

Microsoft PowerPoint - java2 [호환 모드]

PowerPoint Presentation

제이쿼리 (JQuery) 정의 자바스크립트함수를쉽게사용하기위해만든자바스크립트라이브러리. 웹페이지를즉석에서변경하는기능에특화된자바스크립트라이브러리. 사용법 $( 제이쿼리객체 ) 혹은 $( 엘리먼트 ) 참고 ) $() 이기호를제이쿼리래퍼라고한다. 즉, 제이쿼리를호출하는기호

1. auto_ptr 다음프로그램의문제점은무엇인가? void func(void) int *p = new int; cout << " 양수입력 : "; cin >> *p; if (*p <= 0) cout << " 양수를입력해야합니다 " << endl; return; 동적할

03-JAVA Syntax(2).PDF

Microsoft PowerPoint - 13_UMLCoding(2010).pptx

ch09

거리계산문제 간단한문제 n 2 차원평면상의두점사이의거리를구하는프로그램을작성해보자 n 해법 : 피타고라스정리 거리구하는공식 n 두점 (x 1, y 1 ) 과 (x 2, y 2 ) 사이의거리 d n 제곱근은 Math.sqrt, 제곱은 Math.ow 로구함 4 절차지향적거리


단위: 환경정책 형산강살리기 수중정화활동 지원 10,000,000원*90%<절감> 형산강살리기 환경정화 및 감시활동 5,000,000원*90%<절감> 9,000 4, 민간행사보조 9,000 10,000 1,000 자연보호기념식 및 백일장(사생,서예)대회 10

Java ...

JMF2_심빈구.PDF

거창전문대학훈령182.hwp

Spring Boot/JDBC JdbcTemplate/CRUD 예제

ilist.add(new Integer(1))과 같이 사용하지 않고 ilist.add(1)과 같이 사용한 것은 자바 5.0에 추가된 기본 자료형과 해당 객체 자료 형과의 오토박싱/언박싱 기능을 사용한 것으로 오토박싱이란 자바 컴파일러가 객체를 요구하는 곳에 기본 자료형

Transcription:

Refactoring 소개(Ⅱ) (나쁜 디자인의 코드를 좋은 디자인으로 바꾸는 방법) 2013.01.14 JSEA 이재근(jea910124@gmail.com)

2 / 88 Refactoring의 구분 Refactoring 소개 Refactoring의 정의, Refactoring을 하는 이유 Refactoring을 사용하는 적절 / 부적절한 시기, Refactoring에서의 냄새 클래스 내부의 냄새 및 Refactoring 기법 측정할 수 있는 냄새, 이름, 불필요한 복잡성, 중복, 조건 로직 클래스 사이의 냄새 및 Refactoring 기법 데이터, 상속, 책임, 변경 수용하기, 라이브러리 클래스 기타 Refactoring Eclipse에서의 Refactoring 지원 결론 물리적 재구성과 재명명, 클래스 관계 재정의, 클래스 내부에서 코드 변경 요약, 참고자료

클래스 내부의 냄새 및 Refactoring 기법 1. 측정할 수 있는 냄새 2. 이름 3. 불필요한 복잡성 4. 중복 5. 조건 로직 6. 요약

1. 측정할 수 있는 냄새 4 / 88 1. 측정할 수 있는 냄새 긴 메소드 거대한 클래스 많은 매개변수 리스트 주석

1. 측정할 수 있는 냄새 5 / 88 1.1 긴 메소드 : 원인 메소드가 많은 줄을 차지하지 않아야 유지보수가 용이하다. 형사 콜롬보라는 영화가 있다. 이 영화에서 콜롬보는 언제나 아차차, 한가지만 더 라고하며 질문한다. 이처럼 어떤 프로그래머도 적절한 시점에 메소드의 흐름을 끊는 것 보다 메소드를 한가지씩 점점 추가 하다보니, 긴 메소드가 발생하게 된다.

1. 측정할 수 있는 냄새 6 / 88 1.1 긴 메소드 : Refactoring(1/2) Extract Method를 이용해 메소드를 좀 더 작은 부분으로 쪼갠다. (의미론적으로 중요한 메소드들을 뽑아내야 한다) 메소드 나누기를 시작하기도 전에 다른 리팩토링을 발견 할 수도 있다. (일직선으로 길게 나열된 코드 줄, 조건문 등을 정리)

1. 측정할 수 있는 냄새 7 / 88 1.1 긴 메소드 : Refactoring(2/2) Extract Method 그룹으로 함께 묶을 수 있는 코드 조각이 있을 때 코드의 목적이 잘 드러나도록 메소드의 이름을 지어 별도의 메소드로 뽑아내는 작업 수정 전 void printowing(double amount) { printbanner(); //상세 정보 표시 System.out.println( name : + name); System.out.println( amount : + amount); 수정 후 void printowing(double amount) { printbanner(); printdetails(amount); void printdetails(double amount) { System.out.println( name : + name); System.out.println( amount : + amount);

1. 측정할 수 있는 냄새 8 / 88 1.2 거대한 클래스 : 원인 클래스의 코드 크기(Volume)가 관리가능(Manageable)하여야 유지보수하기 쉽다. 클래스의 코드가 클수록 하나 이상의 역할을 수행할 확률이 높다. 클래스가 커지는 데에는 프로그래머가 하나의 클래스에 많은 일을 하게 하려 할 때 나타나는데, 이는 각 부분에 대한 통찰력 부족이 원인일 수 있다.

1. 측정할 수 있는 냄새 9 / 88 1.2 거대한 클래스 : Refactoring(1/6) 이 클래스가 맡고 있는 역할의 일부를 갖는 새로운 클래스를 찾아낼 수 있다면 Extract Class를 이용한다 해당 클래스와 새로운 하위 클래스 간의 역할을 구분할 수 있다면 Extract Subclass를 이용한다. 클라이언트가 사용하는 기능들 중 일부를 따로 묶을 수 있다면 Extract Interface를 이용한다.

1. 측정할 수 있는 냄새 10 / 88 1.2 거대한 클래스 : Refactoring(2/6) Extract Class 두 개의 클래스가 해야 할 일을 하나의 클래스가 하고 있는 경우 새로운 클래스를 만들어서 관련 있는 필드와 메소드를 예전 클래 스에서 새로운 클래스로 옮기는 작업

1. 측정할 수 있는 냄새 11 / 88 1.2 거대한 클래스 : Refactoring(3/6) Extract Class 수정 전 class Person... public String getname() { return name; public String gettelephonenumber() { return ( ( + officeareacode + ) + officenumber); String getoffice AreaCode() { return officeareacode; String getofficenumber() { return officenumber; void setofficenumber (String arg) { officenumber = arg; private String name; private String officeareacode; private String officenumber; 수정 후 class Person... public String getname() {... public String gettelephonenumber() { return officetelephone.gettelephonenumber(); TelephoneNumber getofficetelephone() { return officetelephone; private String name; private TelephoneNumber officetelephone = new TelephoneNumber(); class TelephoneNumber... public String gettelephonenumber() {... String getareacode() { return areacode; void setareacode(string arg) { areacode = arg; String getnumber() { number = arg; void setnumber(string arg) { number = arg; private String number; private String areacode;

1. 측정할 수 있는 냄새 12 / 88 1.2 거대한 클래스 : Refactoring(4/6) Extract Subclass 어떤 클래스가 일부 인스턴스에 의해서만 사용되는 기능을 가지고 있다면 인스턴스만 사용하는 기능을 담당하는 서브클래스를 만드는 작업 수정 전 public class Registration { public NonRegistrationAction Action; public int RegistrationTotal; public string Notes; public string Description; public DateTime RegistrationDate; 수정 후 public class Registration { public int RegistrationTotal; public string Description; public DateTime RegistrationDate; public class NonRegistration extends Registration { public NonRegistrationAction Action; public string Notes;

1. 측정할 수 있는 냄새 13 / 88 1.2 거대한 클래스 : Refactoring(5/6) Extract Interface 여러 클라이언트가 한 클래스 인터페이스의 동일한 부분집합을 사용하고 있거나, 두 클래스가 공통된 인터페이스를 갖는 부분이 있다면 그 부분집합을 인터페이스로 뽑아내는 작업

1. 측정할 수 있는 냄새 14 / 88 1.2 거대한 클래스 : Refactoring(6/6) Extract Interface class Employee... 수정 전 double charge(employee emp, int days) { int base = emp.getrate() * days; if(emp.hasspecialskill()) return base * 1.05; else return base; 수정 후 interface Billable { public int getrate(); public boolean hasspecialskill(); class Employee implements Billable... double charge(billable emp, int days) { int base = emp.getrate() * days; if(emp.hasspecialskill()) return base * 1.05; else return base;

1. 측정할 수 있는 냄새 15 / 88 1.3 많은 매개변수 리스트 : 원인 5개 이내의 매개변수는 유지보수가 용이하다 객체 간의 결합도(Coupling)를 최소화하려고 했을 가능성이 있다. 결합도를 최소화하면 모듈의 독립성을 높이므로 좋은 설계라고 판단하지만, 그렇다고 매개변수 갯수를 늘리면 유지보수가 어려워진다.

1. 측정할 수 있는 냄새 16 / 88 1.3 많은 매개변수 리스트 : Refactoring(1/5) 이미 알고 있는 다른 객체로부터 해당 매개변수 값을 얻을 수 있다면, Replace Parameter with Method를 이용한다. 해당 매개변수들이 하나의 객체로부터 넘어온다면, Preserve Whole Object를 시도한다. 매개변수 데이터가 하나의 논리적 객체로부터 얻어지는 것이 아니라면 Introduce Parameter Object를 이용하여 이들을 그룹으로 한다.

1. 측정할 수 있는 냄새 17 / 88 1.3 많은 매개변수 리스트 : Refactoring(2/5) Replace Parameter with Method 객체가 메소드를 호출한 다음, 결과를 다른 메소드에 대한 매개변수로 넘기고 있다. 수신자 또한 이 메소드를 호출할 수 있다면, 그 매개변수를 제거하고 수신자가 그 메소드를 호출하도록 하는 작업

1. 측정할 수 있는 냄새 18 / 88 1.3 많은 매개변수 리스트 : Refactoring(3/5) Replace Parameter with Method 수정 전 public double getprice() { int baseprice = quantity * itemprice; int discountlevel; if (quantity > 100) discountlevel = 2; else discountlevel = 1; double finalprice = discountedprice (baseprice, discountlevel); return finalprice; 수정 후 private double getdiscountedprice() { if(getdiscountlevel() == 2) return getbaseprice() * 0.1; else return getbaseprice() * 0.05; private double getbaseprice() { return quantity * itemprice; private double discountedprice (int baseprice, int discountlevel) { if(discountlevel ==2) return baseprice * 0.1; else return baseprice * 0.05; private int getdiscountlevel() { if (quantity > 100) return 2; else return 1;

1. 측정할 수 있는 냄새 19 / 88 1.3 많은 매개변수 리스트 : Refactoring(4/5) Preserve Whole Object 어떤 객체에서 여러 개의 값을 얻은 다음 메소드를 호출하면서 매개변수로 넘기고 있다면 대신 그 객체를 매개변수로 넘기는 작업 수정 전 int low = daystemprange.getlow(); int high = daystemprange.gethigh(); withinplan = plan.withinrange(low, high); 수정 후 withinplan = plan.withinrange(daystemprange);

1. 측정할 수 있는 냄새 20 / 88 1.3 많은 매개변수 리스트 : Refactoring(5/5) Introduce Parameter Object 자연스럽게 몰려다니는 매개변수 그룹을 가지고 있다면 그것들을 객체로 바꾸는 작업 수정 전 public class Registration { public void Create(int amount, Student student, IEnumerable<Course> courses) { // do work 수정 후 public class RegistrationContext { public int amount; public Student student; public IEnumerable<Course> courses; public class Registration { public void Create(RegistrationContext registrationcontext) { // do work

1. 측정할 수 있는 냄새 21 / 88 1.4 주석 : 원인 코드에 주석이 있으면 유지보수가 용이해진다. 단, 주석이 적재적소에 배치되어있어야 한다. 코드 내에 잔 주석이 많을 때 메소드의 구현이 깔끔하지 못하므로 잔 주석이 많아진다. 메소드에 대한 설명이 한군데에 몰려있으면 코드의 이해가 어려워진다. 이럴 때는 주석을 충분히 활용하여 해결할 수 있다.

1. 측정할 수 있는 냄새 22 / 88 1.4 주석 : Refactoring(1/4) 코드의 일정 블록을 설명하는 경우 Extract Method를 통해 그 블록을 별도의 메소드로 끄집어 낼 수 있다. 주석이 메소드가 하는 일을 현재의 메소드 이름보다 잘 설명 할 때 Rename Method를 이용하여 메소드의 이름을 바꾼다. 주석이 선 조건을 설명한다면 Introduce Assertion을 이용하 여 주석을 코드로 대체하는 방안을 고려해 본다.

1. 측정할 수 있는 냄새 23 / 88 1.4 주석 : Refactoring(2/4) Extract Method 그룹으로 함께 묶을 수 있는 코드 조각이 있을 때 코드의 목적이 잘 드러나도록 메소드의 이름을 지어 별도의 메소드로 뽑아내는 작업 수정 전 void printowing(double amount) { printbanner(); //상세 정보 표시 System.out.println( name : + name); System.out.println( amount : + amount); 수정 후 void printowing(double amount) { printbanner(); printdetails(amount); void printdetails(double amount) { System.out.println( name : + name); System.out.println( amount : + amount);

1. 측정할 수 있는 냄새 24 / 88 1.4 주석 : Refactoring(3/4) Rename Method 메소드의 이름이 그 목적을 드러내지 못하고 있을 때 메소드의 이름을 바꾸는 작업 Ex) getinvcdtlmt() getinvoiceablecreditlimit()

1. 측정할 수 있는 냄새 25 / 88 1.4 주석 : Refactoring(4/4) Introduce Assertion 코드의 한 부분이 프로그램 상태에 대해 어떤 것을 가정하고 있을 때 Assertion을 써서 가정을 명시되게(explicit)하는 작업 수정 전 double getexpenselimit() { return(expenselimit!= NULLEXPENSE)? expenselimit; primaryproject.getmemberexpenselimit(); 수정 후 double getexpenselimit() { Assert.isTrue(expenseLimit!= NULLEXPENSE primaryproject!= null); return(expenselimit!= NULLEXPENSE)? expenselimit; primaryproject.getmemberexpenselimit();

2. 이름 26 / 88 2. 이름 타입이 내장되어 있는 이름 의사소통을 방해하는 이름 일관성 없는 이름

2. 이름 27 / 88 2.1 타입이 내장되어 있는 이름 : 원인 이름에 타입을 내장하면 유지보수가 어려워진다. 타입이 내장되어 있는 이름에서는 인수와 이름이 둘 다 같은 타입을 나타내기 때문에 중복의 문제가 있다. 의사소통을 위해 이름에 타입을 추가하는 경우가 있다. (ex. schedule.addcourse(course))

2. 이름 28 / 88 2.1 타입이 내장되어 있는 이름 : Refactoring Rename Method를 이용해, 타입에 연관되지 않으면서도 의도를 잘 전달할 수 있는 이름으로 변경한다.

2. 이름 29 / 88 2.2 의사소통을 방해하는 이름 : 원인 명확한 이름이어야 유지보수가 쉬워진다. 아래와 같은 내용으로 이름을 결정할 때 문제가 발생한다 하나 또는 두 개의 문자로 이루어진 이름 모음이 생략된 이름 이상한 약어 오해하게 만드는 이름

2. 이름 30 / 88 2.2 의사소통을 방해하는 이름 : Refactoring Rename Method를 사용하여 더 좋은 이름을 부여한다. 메소드뿐만 아니라 패키지, 클래스, 필드, 상수 등도 변경할 수 있다.

2. 이름 31 / 88 2.3 일관성 없는 이름 : 원인 이름들이 주제를 따르지 않으면 유지보수가 어려워진다. 같은 기능에 대해 이쪽저쪽에서 다른 이름을 사용하는 경우 간혹 개발자들이 자신들이 추가한 것을 구분하기 위해 일관성이 없는 다른 이름을 사용하는 경우도 있다.

2. 이름 32 / 88 2.3 일관성 없는 이름 : Refactoring 가장 좋은 이름을 골라서 Rename Method를 사용해 같은 것들에는 같은 이름을 부여한다. 이러한 작업을 마치고 나면 이전보다 더 비슷해 보이는 클래스들을 발견할 수도 있을 것이다. 중복의 냄새를 찾아보고, 있다면 제거하라.

3. 불필요한 복잡성 33 / 88 3. 불필요한 복잡성 죽은 코드 추측성 일반화

3. 불필요한 복잡성 34 / 88 3.1 죽은 코드 : 원인 및 Refactoring 사용되지 않는 코드를 살려두면 유지보수에 어려움이 따른다. 요구사항이 변했거나 새로운 접근 방식을 도입하였으나 이전 코드를 정리하지 않아서 변수, 매개변수, 필드, 코드의 일부분, 메소드 혹은 클래스가 어떤 곳에서도 사용되지 않을 때 사용되지 않는 코드와 이와 관련된 테스트를 삭제한다.

3. 불필요한 복잡성 35 / 88 3.2 추측성 일반화 : 원인 코드가 요구사항을 충족할 때가 유지보수하기 좋을 때다. 미래의 요구사항을 대응하기 위해 코드의 내용을 추가하다 보면 결과적으로 쓰이지 않는 코드가 만들어 질 때가 있다. 결국 코드가 현재 구현된 요구사항에 대해 필요 이상으로 복잡해진다.

3. 불필요한 복잡성 36 / 88 3.2 추측성 일반화 : Refactoring(1/9) 필요 없는 메소드는 Inline Method를 이용해 정리한다. 필요 없는 필드는 이 필드에 대한 레퍼런스가 없는지 확인한 후 없다면 제거한다. 필요 없는 매개변수는 Remove Parameter를 이용해 정리한다.

3. 불필요한 복잡성 37 / 88 3.2 추측성 일반화 : Refactoring(2/9) 필요 없는 클래스의 행위가 있어야 할 곳이 해당 클래스의 부모 클래스나 자식 클래스로 판단되면, Collapse Hierarchy를 이용해 그 부모 클래스 또는 자식 클래스 중 하나로 포개어 넣는다. 위와 같지 않다면 Inline Class를 이용하여 해당 클래스의 행위를 그 호출자 안으로 포개어 넣는다.

3. 불필요한 복잡성 38 / 88 3.2 추측성 일반화 : Refactoring(3/9) Inline Method 메소드의 본문이 메소드 이름만큼이나 명확하다면 해당 본문을 메소드를 호출하는 호출자 안으로 옮기고 메소드를 삭제하는 작업 수정 전 int getrating() { return (morethanfivelatedeliveries())? 2: 1; boolean moretheanfivelatedeliveries() { return numberoflatedeliveries > 5; 수정 후 int getrating() { return (numberoflatedeliveries > 5)? 2: 1;

3. 불필요한 복잡성 39 / 88 3.2 추측성 일반화 : Refactoring(4/9) Remove Parameter 파라미터가 메소드 몸체에서 더 이상 사용되지 않는다면 그 파라미터를 제거하는 작업

3. 불필요한 복잡성 40 / 88 3.2 추측성 일반화 : Refactoring(5/9) Remove Parameter 수정 전 public class Vehicle { public Vehicle(string make, string model, string type) { Make = make; Model = model; public static Vehicle CreateNew(string make, string model) { return new Vehicle(make, model, String.Empty); public string Make; public string Model; 수정 후 public class Vehicle { public Vehicle(string make, string model) { Make = make; Model = model; public static Vehicle CreateNew(string make, string model) { return new Vehicle(make, model); public string Make; public string Model;

3. 불필요한 복잡성 41 / 88 3.2 추측성 일반화 : Refactoring(6/9) Collapse Hierarchy 수퍼클래스와 서브클래스가 별로 다르지 않다면 그것들을 하나로 합치는 작업 수정 전 public class Website { public string title; public string description; public IEnumerable<Webpage> pages; public class StudentWebsite extends Website { public boolean isactive; 수정 후 public class Website { public string title; public string description; public IEnumerable<Webpage> pages; public boolean isactive;

3. 불필요한 복잡성 42 / 88 3.2 추측성 일반화 : Refactoring(8/9) Inline Class 클래스가 하는 일이 많지 않은 경우에는 그 클래스에 있는 모든 변수와 메소드를 다른 클래스로 옮기고 그 클래스를 제거하는 작업

3. 불필요한 복잡성 43 / 88 3.2 추측성 일반화 : Refactoring(9/9) Inline Class 수정 전 class Person... private TelephoneNumber officetelephone = new TelephoneNumber; class TelephoneNumber... public String gettelephonenumber() { return ( ( + areacode + ) + number); String getareacode() { return areacode; void SetAreaCode(String arg) { areacode = arg; String getnumber() { return number; void setnumber(string arg) { number = arg; 수정 후 class Person... String getareacode() { return officetelephone.getareacode(); void setareacode(string arg) { officetelephone.setareacode(arg); String getnumber() { return officetelephone.getnumber(); void setnumber(string arg) { officetelephone.setnumber(arg); private String number; private String areacode;

4. 중복 44 / 88 4. 중복 매직 넘버 중복된 코드 다른 인터페이스를 갖는 대체 클래스

4. 중복 45 / 88 4.1 매직 넘버 : 원인 코드내에 상수값이 보일수록 유지보수는 어려워진다. 상수를 코드에 넣었을 때 상수 값으로부터 유도되거나 상수 값에 의존적인 다른 값들에 문제를 가져올 수 있다.

4. 중복 46 / 88 4.1 매직 넘버 : Refactoring(1/2) 매직 넘버를 특별한 값을 갖는 기호 상수로 대체한다. 값이 문자열이면, 이 값을 매핑 도구에 할당하는 것이 좋을 수 있다.

4. 중복 47 / 88 4.1 매직 넘버 : Refactoring(2/2) Replace Magic Number with Symbolic Constant 특별한 의미를 갖는 숫자 리터럴이 있을 때 상수를 만들고, 의미를 잘 나타내도록 이름을 짓고, 숫자를 상수로 바꾸는 작업 수정 전 double potentialenergy(double mass, double height) { return mass * 9.81 * height; 수정 후 double potentialenergy(double mass, double height) { return mass * GRAVITATIONALCOSTANT * height; static final double GRAVITATIONALCONSTANT = 9.81;

4. 중복 48 / 88 4.2 중복된 코드 : 원인 중복이 없는 코드는 유지보수가 쉽다. 프로그래머들이 시스템의 서로 다른 부분에서 독립적으로 작업하면서 거의 동일한 코드를 만들고 있다는 사실을 깨닫지 못할 때 중복이 발생한다. 중복을 가리고 있는 또 다른 냄새를 제거하면 중복이 더 명확해 보인다.

4. 중복 49 / 88 4.2 중복된 코드 : Refactoring (1/9) 하나의 메소드에, 또는 한 클래스의 서로 다른 두 메소드에 중복이 있으면, Extract Method를 이용하여 공통부분을 별도의 메소드로 빼낸다.

4. 중복 50 / 88 4.2 중복된 코드 : Refactoring (2/9) 두 형제클래스에 중복이 있으면, Extract Method를 이용하여 단일 루틴 생성 후 Pull Up Field나 Pull up Method를 이용, 공통부분을 하나로 묶는다. 또한 이 작업을 수행한 후에 Form Template Method를 이용하여 부모 클래스에 공통 알고리즘을 생성하고 자식 클래스에 고유한 구현을 생성할 수도 있다.

4. 중복 51 / 88 4.2 중복된 코드 : Refactoring (3/9) 서로 관련없는 두 클래스에 중복된 코드가 있다면, Extract Class를 이용해 공통부분을 새로운 클래스로 빼내거나, 이 냄새가 기능에 대한 욕심 이 아닌지 검토해 보고, 맞다면 공통 코드가 실제로 하나의 클래스에만 있도록 리팩토링 한다.

4. 중복 52 / 88 4.2 중복된 코드 : Refactoring (4/9) 이들 중 어떤 경우에서든 두 코드가 말 그대로 동일하지는 않지만 같은 기능을 수행하는 경우가 있을 수 있다. 그 경우, Substitute Algorithm을 이용해 하나의 알고리즘 으로 통합할 수 있다.

4. 중복 53 / 88 4.2 중복된 코드 : Refactoring (5/9) Pull Up Field 두 서브클래스가 동일한 필드를 가지고 있다면 그 필드를 수퍼클래스로 옮기는 작업 수정 전 public abstract class Account... public class CheckingAccount extends Account { private int minimumcheckingbalance = 5; public class SavingsAccount extends Account { private int minimumsavingsbalance = 5; 수정 후 public abstract class Account { protected int minimumbalance = 5; public class CheckingAccount extends Account... public class SavingsAccount extends Account...

4. 중복 54 / 88 4.2 중복된 코드 : Refactoring (6/9) Pull Up Method 동일한 일을 하는 메소드를 여러 서브클래스에서 가지고 있다면 그 메소드를 수퍼클래스로 옮기는 작업 수정 전 public abstract class Vehicle... public class Car extends Vehicle { public void turn(direction direction)... public class Motorcycle extends Vehicle { public void turn(direction direction)... 수정 후 public abstract class Vehicle { public void turn(direction direction)... public class Car extends Vehicle... public class Motorcycle extends Vehicle...

4. 중복 55 / 88 4.2 중복된 코드 : Refactoring (7/9) Form Template Method 각각의 서브클래스에 동일한 순서로 비슷한 단계를 진행하지만 단계가 완전히 같지는 않은 두 메소드가 있다면 그 단계를 동일한 시그너처를 가진 메소드로 만드는 작업 위의 작업을 수행시 원래의 두 메소드는 서로 같이지므로 수퍼 클래스로 올릴 수 있다

4. 중복 56 / 88 4.2 중복된 코드 : Refactoring (8/9) Form Template Method 수정 전 class ResidentialSite extends Site { public double getbillableamount() { double base = units * rate; double tax = base * Site.TAX_RATE; return base * tax; class LifelineSite extends Site { public double getbillableamount() { double base = units * rate * 0.5; double tax = base * Site.TAX_RATE * 0.3; return base * tax; 수정 후 abstract class Site { public double getbillableamount() { return getbaseamount() * gettaxamount(); public abstract double getbaseamount(); public abstract double gettaxamount(); class ResidentialSite extends Site { public double getbaseamount() { return units * rate; public double gettaxamount() { return getbaseamount() * Site.TAX_RATE; class LifelineSite extends Site...

4. 중복 57 / 88 4.2 중복된 코드 : Refactoring (9/9) Substitute Algorithm 알고리즘을 보다 명확한 것으로 바꾸고 싶을 때 메소드의 몸체를 새로운 알고리즘으로 바꾸는 작업 수정 전 String foundperson(string[] people) { for(int i = 0; i<people.length; i++) { if(people[i].equals( Don )) { return Don ; if(people[i].equals( John )) { return John ; if(people[i].equals( Kent )) { return Kent ; return ; 수정 후 String foundperson(string[] people) { List candidates = Arrays.asList(new String[] { Don, John, Kent ); for(int i = 0; i < people.length; i++) { if(candidates.contains(people[i]) { return people[i]; return ;

4. 중복 58 / 88 4.3 다른 인터페이스를 갖는 대체 클래스 : 원인 클래스가 하는 일은 거의 같지만, 서로 다른 인터페이스를 가지고 있으면 유지보수에 혼동이 온다. 프로그래머가 비슷한 상황을 다루기 위해 비슷한 코드 를 생성하지만 다른 코드가 존재한다는 것을 인식하지 못해서 같은 일을 하는 두 클래스에 사용된 메소드 이름 이 서로 다르게 된다.

4. 중복 59 / 88 4.3 다른 인터페이스를 갖는 대체 클래스 : Refactoring(1/6) 클래스 중 하나를 제거할 수 있도록 클래스들을 조율한다. 1) Rename Method를 이용해 메소드 이름을 유사하게 만든다. 2) Move Method, Add Parameter, Parameterize Method를 이용 하여 프로토콜을 유사하게 만든다. 3) 두 클래스가 유사하기는 하지만 동일하지는 않다면, 클래스들을 적 절히 조율 한 후 Extract Superclass를 사용한다. 4) 가능하면 나머지 클래스는 삭제한다.

4. 중복 60 / 88 4.3 다른 인터페이스를 갖는 대체 클래스 : Refactoring(2/6) Move Method 메소드가 자신이 정의된 클래스보다 다른 클래스의 기능을 더 많이 사용하고 있을 때 이 메소드를 가장 많이 사용하고 있는 클래스에 비슷한 몸체를 가진 새로운 메소드를 만들고, 이전 메소드는 간단한 위임으로 바꾸거나 완전히 제거하는 작업

4. 중복 61 / 88 4.3 다른 인터페이스를 갖는 대체 클래스 : Refactoring(3/6) Move Method 수정 전 class Account... private AccountType type; private int daysoverdrawn; double overdraftcharge() { if(type.ispremium()) { double result = 10; if(daysoverdrawn > 7) { result += (daysoverdrawn - 7) * 0.88; return result; else return daysovedrawn * 1.75; double bankcharge() { double result = 4.5; if(daysoverdrawn > 0) { result += overdraftcharge(); return result; 수정 후 class AccountType... double overdraftcharge(int daysoverdrawn) { if(ispremium()) { double result = 10; if(daysoverdrawn > 7) { result += (daysoverdrawn - 7) * 0.88; return result; else return daysovedrawn * 1.75; class Account... double bankcharge() { double result = 4.5; if(daysoverdrawn > 0) { result += type.overdraftcharge(); return result;

4. 중복 62 / 88 4.3 다른 인터페이스를 갖는 대체 클래스 : Refactoring(4/6) Add Parameter 어떤 메소드가 그 메소드를 호출하는 부분에서 더 많은 정보를 필요로 할 때 이 정보를 넘길 수 있는 객체에 대한 파라미터를 추가하는 작업 수정 전 public class Customer { public void customerinfo() { customeraddress(); public abstract void customeraddress(); 수정 후 public class MyBaseClass { public void customerinfo() { int zipcode; customerzipcode(zipcode); protected void customerzipcode(int zipcode) { customeraddress(); public abstract void customeraddress();

4. 중복 63 / 88 4.3 다른 인터페이스를 갖는 대체 클래스 : Refactoring(5/6) Parameterize Method 몇몇 메소드가 메소드 몸체에 다른 값을 포함하고 있는 것을 제외 하고는 비슷한 일을 하고 있을 때 다른 값을 파라미터로 넘겨 받는 하나의 메소드를 만드는 작업 수정 전 class Employee { void tenpercentraise() { salary *= 1.1; void fivepercentraise() { salary *= 1.05; 수정 후 void raise (double factor) { salary *= (1+factor);

4. 중복 64 / 88 4.3 다른 인터페이스를 갖는 대체 클래스 : Refactoring(6/6) Extract Superclass 비슷한 메소드와 필드를 가진 두 개의 클래스가 있을 때 수퍼클래스를 만들어서 공통된 메소드와 필드를 수퍼클래스로 옮기는 작업 public class Dog { public void eatfood() { // eat some food public void groom() { // grooming 수정 전 수정 후 public class Animal { public void eatfood()... public void groom()... public class Dog extends Animal { //do work

5. 조건 로직 65 / 88 5. 조건 로직 null 체크 복잡한 Boolean 표현식 특별 케이스 가장된 상속

5. 조건 로직 66 / 88 5.1 null 체크 : 원인 조건문이 적을수록 유지보수가 쉬워진다. null을 기본값으로 나타내기 위해 사용하거나, 예상치 못한 경우에 null을 사용하거나, null포인터 버그를 처리하기 위해 사용하다 보니 코드 내에 많은 null 체크가 존재하게 된다.

5. 조건 로직 67 / 88 5.1 null 체크 : Refactoring(1/3) 사용할 만한 기본 값이 있다면 null 대신 그 기본 값을 사용한다. 그렇지 않다면 Introduce Null Object를 이용해 명시적으로 사용하는 기본 객체를 만든다.

5. 조건 로직 68 / 88 5.1 null 체크 : Refactoring(2/3) Introduce Null Object null 체크를 반복적으로 하고 있을 때 null 값을 null 객체로 대체하는 작업

5. 조건 로직 69 / 88 5.1 null 체크 : Refactoring(3/3) Introduce Null Object 수정 전 public class NavigationApplet extends Applet { public boolean mousemove( Event event, int x, int y ) { if ( mouseeventhandler!= null ) return mouseeventhandler.mousemove(graphicscontext, event, x, y ); return true; public boolean mousedown( Event event, int x, int y ) { if ( mouseeventhandler!= null ) return mouseeventhandler.mousedown( graphicscontext, event, x, y ); return true;... 수정 후 public class NullMouseEventHandler extends MouseEventHandler { public boolean mousemove( metagraphicscontext mgc, Event event, int x, int y ) { return true;... public class NavigationApplet extends Applet { private MouseEventHandler mouseeventhandler = new NullMouseEventHandler(); public boolean mousemove( Event event, int x, int y ) { return mouseeventhandler.mousemove( graphicscontext, event, x, y );...

5. 조건 로직 70 / 88 5.2 복잡한 Boolean 표현식 : 원인 조건식이 간결해야 유지보수가 쉽다 코드가 처음부터 복잡할 때나, 코딩을 하면서 여러 조건들이 추가되면서 코드 내에 and, or, not 으로 이루어진 복잡한 조건들이 생기게 될 수 있다.

5. 조건 로직 71 / 88 5.2 복잡한 Boolean 표현식 : Refactoring(1/3) De Morgan s Law을 적용한다.(AND 연산과 OR연산을 서로 바꾸고 각 변수의 보수(부정)을 취한다) 각 조건절을 좀 더 명쾌하게 하기 위해 Introduce Explaining Variable을 사용한다. 괄호 절을 이용해 특정 조건을 떼어내면 나머지 조건절이 더 단순하게 된다. Decompose Conditional을 이용하여 각 조건절을 그 나름의 메소드로 빼낸다.

5. 조건 로직 72 / 88 5.2 복잡한 Boolean 표현식 : Refactoring(2/3) Introduce Explaining Variable 복잡한 수식이 있을 때 수식의 결과나 수식의 일부에 자신의 목적을 잘 설명하는 이름으로 된 임시변수를 사용하는 작업 수정 전 if ((platform.touppercase().indexof( MAC ) > -1 && (browser.touppercase().indexof( IE ) > -1 && wasinitialized() && resized > 0) { //do work 수정 후 final boolean ismacos = platform.touppercase().indexof( MAC ) > -1; final boolean isiebrowser = browser.touppercase().indexof( IE ) >-1; final boolean wasresized = 0; if(ismacos && isiebrowser && wasinitialized() && wasresized) { //do work

5. 조건 로직 73 / 88 5.2 복잡한 Boolean 표현식 : Refactoring(3/3) Decompose Conditional 복잡한 조건문이 있을 때 조건, then 부분, else 부분에서 메소드를 추출하는 작업 수정 전 if (date.before (SUMMER_START) date.after(summer_end)) { charge = quantity * winterrate + winterservicecharge; else { charge = quantity * summerrate; 수정 후 if (notsummer(date)) { charge = wintercharge(quantity); else { charge = summercharge(quantity);

5. 조건 로직 74 / 88 5.3 특별 케이스 : 원인 조건문이 많고, 그 조건절들이 비슷할수록 유지보수에 혼동이 온다. 필요하다고 생각할 때, 복잡한 if문을 사용하거나 특정 값을 체크(열거형 또는 상수 비교를 위해)하게 된다.

5. 조건 로직 75 / 88 5.3 특별 케이스 : Refactoring(1/3) 조건절이 다형성을 대신하고 있다면, Replace Conditional with Polymorphism을 이용한다. If / then 절들이 아주 유사하다면, 이들을 재작성하여 같은 코드 조각이 각각의 경우에 적절한 결과를 생성할 수 있도록 한다. 그러면 조건절을 제거할 수 있다.

5. 조건 로직 76 / 88 5.3 특별 케이스 : Refactoring(2/3) Replace Conditional with Polymorphism 객체의 타입에 따라 다른 동작을 선택하는 조건문을 가지고 있을 때 조건문의 각 부분을 서브클래스에 있는 오버라이딩 메소드로 옮기고, 원래 메소드를 abstract로 만드는 작업

5. 조건 로직 77 / 88 5.3 특별 케이스 : Refactoring(3/3) Replace Conditional with Polymorphism 수정 전 class Employee { public double payamount() if (_type = Employee.ENGINEER) { return _monthlysalary; if (_type = Employee.MANAGER) { return _monthlysalary * 2; if (_type = Employee.ENGINEER) { return _monthlysalary/2; 수정 후 class Employee { public double payamount() { return _type.payamount(this); abstract class EmployeeType { pubic abstract double payamount(employee x); class Engineer extends EmployeeType { pubic double payamount(employee anemployee) { return anemployee.monthlysalary(); class Manager extends EmployeeType { pubic double payamount(employee anemployee) { return anemployee.monthlysalary() * 2;...

5. 조건 로직 78 / 88 5.4 가장된 상속 : 원인 상속관계를 철저히 해야 유지보수가 쉽다. 새로운 클래스를 도입하는 것을 게을리 하여 발생한다. 새로운 클래스를 작성하기보다 if문이나 switch문을 우선 사용하기 때문에 발생한다.(코드를 정리하기보다 두 번째 switch문을 이용하는 등)

5. 조건 로직 79 / 88 5.4 가장된 상속 : Refactoring(1/3) 동일한 조건에 대한 switch문이 여러 곳에서 사용될 때 1) Extract Method. 각 조건절에서 코드를 추출한다 2) Move Method. 연관된 코드를 올바른 클래스로 옮긴다. 3) Replace Type Code with Subclass 또는 Replace Type Code with State/Strategy. 상속 구조를 만든다. 4) Replace Conditional with Polymorphism. 조건절을 삭제한다.

5. 조건 로직 80 / 88 5.4 가장된 상속 : Refactoring(2/3) 여러 개의 조건절이 한 클래스에서 발생한다면, Replace Parameter with Explicit Methods 또는 Introduce Null Object를 이용해 조건 로직을 대체할 수 있다.

5. 조건 로직 81 / 88 5.4 가장된 상속 : Refactoring(3/3) Replace Parameter with Explicit Methods 파라미터 값에 따라서 다른 코드를 실행하는 메소드가 있을 때 각각의 파라미터 값에 대한 별도의 메소드를 만드는 작업 수정 전 static Employee create(int type) { switch (type) { case ENGINEER: return new Engineer(); case SALESMAN: return new Salesman(); case MANAGER: return new Manager() default: throw new IllegalArgumentException ("Incorrect type code value"); 수정 후 static Employee createengineer() { return new Engineer(); static Employee createsalesman() { return new Salesman(); static Employee createmanager() { return new Manager(); static Employee create(int type) { switch (type) { case ENGINEER: return Employee.createEngineer();...

6. 요약 82 / 88 6. 요약 측정할 수 있는 냄새 이름 불필요한 복잡성 중복 조건 로직

6. 요약 83 / 88 6.1 측정할 수 있는 냄새 냄새의 종류 긴 메소드 Refactoring 기법 Extract Method 거대한 클래스 긴 매개변수 리스트 주석 Extract Class, Extract Subclass Extract Interface Replace Parameter with Method Preserve Whole Object Introduce Parameter Object Extract Method Rename Method Introduce Assertion

6. 요약 84 / 88 6.2 이름 냄새의 종류 Refactoring 기법 타입이 내장되어 있는 이름 의사소통을 방해하는 이름 Rename Method Rename Field Rename Constant 일관성 없는 이름

6. 요약 85 / 88 6.3 불필요한 복잡성 냄새의 종류 Refactoring 기법 죽은 코드 사용되지 않는 코드 및 관련 텍스트 삭제 추측성 일반화 Collapse Hierarchy Inline Class Inline Method Remove Method Remove Parameter 필요없는 필드 제거

6. 요약 86 / 88 6.4 중복 냄새의 종류 매직 넘버 Refactoring 기법 Replace Magic Number with Symbolic Constant 중복된 코드 다른 인터페이스를 갖는 대체 클래스 Extract Method Pull Up Field / Method Form Template Method Extract Class Rename Method Move Method, Add Parameter, Parameterize Method Extract Superclass 나머지 클래스 삭제

6. 요약 87 / 88 6.5 조건 로직 냄새의 종류 null 체크 Refactoring 기법 Introduce Null Object 복잡한 Boolean 표현식 Introduce Explaining Variable Decompose Conditional 특별 케이스 가장된 상속 Replace Conditional with Polymorphism 조건절에서 코드 추출 추출한 코드를 올바른 클래스로 이동 상속 구조 생성 조건절 삭제 Replace Parameter with Explicit Methods

88 / 88 Refactoring의 구분 Refactoring 소개 Refactoring의 정의, Refactoring을 하는 이유 Refactoring을 사용하는 적절 / 부적절한 시기, Refactoring에서의 냄새 클래스 내부의 냄새 및 Refactoring 기법 측정할 수 있는 냄새, 이름, 불필요한 복잡성, 중복, 조건 로직 클래스 사이의 냄새 및 Refactoring 기법 데이터, 상속, 책임, 변경 수용하기, 라이브러리 클래스 기타 Refactoring Eclipse에서의 Refactoring 지원 결론 물리적 재구성과 재명명, 클래스 관계 재정의, 클래스 내부에서 코드 변경 요약, 참고자료