DIARY

자바로 배우는 리팩토링 입문 - 유키 히로시 리뷰 (2)

혀내 2022. 4. 12. 13:09
반응형

리팩토링 방법

  • 1 - 4번째 방법은 아래 링크에서 확인할 수 있습니다.
 

자바로 배우는 리팩토링 입문 - 유키 히로시 리뷰 (1)

코드에서 나는 악취를 나타내는 말 겹치잖아! 너무 길어! 너무 많아! 이름이 안 맞잖아! 너무 공개적이잖아! 객체 지향답지 않아! 리팩토링 방법 1. 매직 넘버를 기호 상수로 치환 if (100 < input.lengt

aeliketodo.tistory.com

 

5. 메서드 추출

public void print (int times) {
    // 테두리 출력
    System.out.print("+");
    for (int i = 0; i < _content.length(); i++) {
    	System.out.print("-");
    }
    System.out.println("+");
    
    // 내용 출력
    for (int i = 0; i < times; i++) {
    	System.out.println("|" + _content + "|");
    }
    
    // 테두리 출력
    System.out.print("+");
    for (int i = 0; i < _content.length(); i++) {
    	System.out.print("-");
    }
    System.out.println("+")
}

public void print (int times) {
    printBorder();
    printContent(times);
    printBorder();
}

private void printBorder() {
    System.out.print("+");
    for (int i = 0; i < _content.length(); i++) {
    	System.out.print("-");
    }
    System.out.println("+");
}

private void printContent(int times) {
    for (int i = 0; i < times; i++) {
    	System.out.println("|" + _content + "|");
    }
}

 

역 리팩토링 - 메서드 인라인화

  • 너무 짧은 메서드들은 오히려 호출 부분에 직접 작성해 메서드의 개수를 줄인다.

 

 

6. 클래스 추출

public class Book {
    private String _title;
    private String _isbn;
    private String _price;
    private String _authorName;
    private String _authorMail;
    ....
}

public class Book {
    private String _title;
    private String _isbn;
    private String _price;
	private Author _author;
    ....
}

class Author {
	private String _name;
    private String _mail;
    ....
}

 

주의 사항

양방향 링크는 피한다.

  •  Book 클래스 → Author 클래스의 단방향 링크를 굳이.. 역방향 링크로 만들어 문제를 일으키지 않도록 한다. 양방향은 되도록이면 단방향으로 변경시킨다.

 

 

 

7. 분류 코드를 클래스로 치환

public class Item {
    public static final int TYPECODE_BOOK = 0;
    public static final int TYPECODE_DVD = 1;
    public static final int TYPECODE_SOFTWARE = 2;
    
    private final int _typecode;
    ....
}

public class ItemType {
    public static final ItemType TYPECODE_BOOK = 0;
    public static final ItemType TYPECODE_DVD = 1;
    public static final ItemType TYPECODE_SOFTWARE = 2;
    ...
}

public class Item {
    private final ItemType _itemtype;
    ...
}

또는 enum을 사용한다.

public enum ItemType {
    BOOK(0),
    DVD(1),
    SOFTWARE(2);
    
    private final int _typecode;
    ...
}

 

 

8. 분류 코드를 하위 클래스로 치환

public class Shape {
    private final int _typecode;
    ...
    
    public void draw() {
    	switch (_typecode) {
        case TYPECODE_LINE:
        	...
            break;
        case TYPECODE_RECTANGLE:
        	...
            break;
        case TYPECODE_OVAL:
        	...
            break;
        default:
        	;
    }
    ...
}

public class Shape {
    ...
    public abstract void draw();
}

public class ShapeLine extends Shape {
	...
    @Override
    public void draw() {
    	...
    }
}

public class ShapeRectangle extends Shape {
	...
    @Override
    public void draw() {
    	...
    }
}

public class ShapeOval extends Shape {
	...
    @Override
    public void draw() {
    	...
    }
}

 

switch 문, instanceof 연산자 == 악취

  • instanceof 연산자, switch 문으로 '클래스에 따른 처리'를 하는 코드는 클래스 별로 나눠 작성하도록 한다.

 

 

9. 분류 코드를 상태/전략 패턴으로 치환

  • 여기서 말하는 상태(전략) 패턴은 하나의 디자인 패턴 방법이다.
public class Logger {
    public static final int STATE_STOPPED = 0;
    public static final int STATE_LOGGING = 0;
    private int _state;
    ...
    
    public Logger() {
    	_state = STATE_STOPPED;
    }
    
    public void start() {
    	switch (_state) {
        case STATE_STOPPED:
        	...
            _state = STATE_LOGGING;
        case STATE_LOGGING:
        	...
        }
    }
}

public abstract class State {
    public abstract int getTypeCode();
}

public class StateStopped extends State {
    @Override
    public int getTypeCode() {
    	return Logger.STATE_STOPPED;
    }
}

public class StateLogging extends State {
    @Override
    public int getTypeCode() {
    	return Logger.SATE_LOGGING;
    }
}

public class Logger {
    public static final int STATE_STOPPED = 0;
    public static final int STATE_LOGGING = 1;
    private State _state;
    
    public void start() {
    	switch (getState()) {
        case STATE_STOPPED:
            ...
            setState(STATE_LOGGING);
            ...
        }
    }
}

 

다형성으로 조건문 분기 치환

  • 상태/전략 패턴으로 치환해도 남아있는 switch 문들을 제거할 수 있는 방법이다

 

1) enum 사용

public enum State {
    STATE_STOPPED,
    STATE_LOGGING,
}

public class Logger {
	...
    public State getState() {
    	return _state;
    }
    
    public void setState(State state) {
    	_state = state;
    }
}

 

2) 상태 의존 코드 → 상태 객체로 이동

public enum State {
    STATE_STOPPED {
    	@Override
        public void start() {
        	System.out.println("** START LOGGING **");
        }
     	
    	@Override
        public void start() {
        	/* 아무것도 하지 않음 */
        }
     	
    	@Override
        public void start() {
        	System.out.println("Ignoring: " + info);
        }	
    },
    
    STATE_LOGGING {
    	@Override
        public void start() {
        	/* 아무것도 하지 않음 */
        }
     	
    	@Override
        public void start() {
        	System.out.println("** STOP LOGGING **");
        }
     	
    	@Override
        public void start() {
        	System.out.println("Logging" + info);
        }	
    };
    
    public abstract void start();
    public abstract void stop();
    public abstract void log(String info);
}

 

반응형