[Java Refactoring] 제어 플래그 삭제

728x90
반응형
SMALL

자바로 배우는 리팩토링 입문

  • Java Refactoring For Beginner
  • 건강한 코드로 소프트웨어 체질을 개선하자!
  • 유키 히로시 지금
  • 길벗 출판사
  • 2017.10.31

 

리팩토링을 위한 스터디 내용을 정리하자.

 

 

 

 


제어 플래그 삭제

제어 플래그(controll flag)란 상태를 기록하고 처리 흐름을 제어하기 위한 boolean 타입 변수를 의미한다.

 

제어 플래그를 지나치게 사용하면 처리 흐름을 파악하기 어려워져서 때로는 프로그램 전체를 파악하는데 어려움을 겪기도 한다.

 

 

Before

public static boolean find(int[] data, int target) {
	boolean flag = false;
    
    for (int i = 0; i < data.length && !flag; i++) {
    	if (data[i] == target) {
            flag = true;
        }
    }
    retur flag;
}

 

After

public static boolean find(int[] data, int target) {
    for (int i = 0; i < data.length && !flag; i++) {
    	if (data[i] == target) {
            return true;
        }
    }
    retur false;
}

 


Before

public class Refactoring {

    public Refactoring(Reader r1) throws IOException {
        BufferedReader r2 = new BufferedReader(r1);
        boolean flag = false;
        String tmp;

        while(!flag) {
            tmp = r2.readLine();
            if (tmp == null) {
                flag = true;
            } else {
                boolean flag2 = true;
                StringBuffer s1 = new StringBuffer();
                StringBuffer s2 = new StringBuffer();

                for (int i = 0; i < tmp.length(); i++) {
                    char tmp2 = tmp.charAt(i);
                    if (flag2) {
                        if (tmp2 == '=') {
                            flag2 = false;
                        } else {
                            s1.append(tmp2);
                        }
                    } else {
                        s2.append(tmp2);
                    }
                }
            }
        }
    }
}

 

After

  • 제어 플래그를 제거하기 전에 변수명을 알기 쉽게 변경하자.
public class Refactoring {

    public Refactoring(Reader r) throws IOException {
        BufferedReader reader = new BufferedReader(r);
        boolean reading = false;
        String line;

        while(!reading) {
            line = reader.readLine();
            if (line == null) {
                reading = true;
            } else {
                boolean scanningKey = true;
                StringBuffer keyBuffer = new StringBuffer();
                StringBuffer valueBuffer = new StringBuffer();

                for (int i = 0; i < tmp.length(); i++) {
                    char c = line.charAt(i);
                    if (scanningKey) {
                        if (c == '=') {
                            scanningKey = false;
                        } else {
                            keyBuffer.append(c);
                        }
                    } else {
                        valueBuffer.append(c);
                    }
                }
            }
        }
    }
}

 

 

 

After

  • 제어 플래그의 역할을 파악하고, 동일한 기능을 제공하는 메서드로 치환하자.
public class Refactoring {

    public Refactoring(Reader r) throws IOException {
        BufferedReader reader = new BufferedReader(r);

        while(true) {
            String line = reader.readLine();
            if (line == null) {
                break;
            }

            int equalIndex = line.indexOf("=");

            if (equalIndex > 0) {
                String key = line.substring(0, equalIndex);
                String value = line.substring(equalIndex + 1, line.length());
            }
        }
    }
}

 

  • 제어 플래그 reading의 역할은 BufferReader의 read()할 line이 있는지 확인하는 것이다.
  • 그렇기에, while문으로 변경하고 read()할 line이 없다면 break를 호출하자.
  • 제어 플래그 scanningKey 의 역할은 "="의 왼쪽을 읽을지, 오른쪽을 읽을지를 구분하는 것이다.
  • 즉, "="의 위치를 파악하는 것이 중요하다.
  • for문과 if문을 통해 "=" 문자를 찾는 부분은 indexOf()로 치환하자.

 

 

After

  • 코드를 더 줄일 수 있는 정규 표현식을 사용하자.
public class Refactoring {

    private static Pattern pattern = Pattern.compile("([^=]+)=(.*)");
    public Refactoring(Reader r) throws IOException {
        BufferedReader reader = new BufferedReader(r);

        while(true) {
            String line = reader.readLine();
            if (line == null) {
                break;
            }

            Matcher matcher = pattern.matcher(line);
            
            if (matcher.matches()) {
                String key = matcher.group(1);
                String value = matcher.group(2);
            }
        }
    }
}

 


연습 문제

2-1. O, X

  • 제어 플래그를 보면 '처리 흐름을 간단히 바꿀 수 없을까?' 하고 생각해 보는 게 좋다. (O)
    • 간단하게 바꾸는 게 좋다.

 

  • 제어 플래그라는 사실을 알기 쉽게 flag라는 이름을 사용하는 게 좋다. (X)
    • flag는 너무 일반적인 이름이다.
    • initialized, error, done, interrupted, found, aborted 등으로 명시하는 게 좋다.

 

  • 반복문 출구는 한 곳에 모으는 게 좋으므로 반복문을 도중에 빠져나오는 break는 사용하지 않는 게 좋다. (X)
    • break를 사용하여 이후 코드를 읽지 않아도 로직을 이해할 수 있게 하는 게 좋다.

 

2-2. 다음 코드의 문제점을 지적하고 개선하시오.

Point[] point;

public boolean existPoint(int x, int y) {
    boolean f = false;
    boolean ff = false;
    
    for (int i = 0; !(f && ff) && i < point.length; i++) {
        if (point[i].x == x) {
            f = true;
        } else {
            f = false;
        }
        
        if (point[i].y == y) {
            ff = true;
        } else {
            ff = false;
        }
    }
    
    boolean fff = false;
    
    if (f && ff) {
        fff = false;
    } else {
        fff = true;
    }

    return fff;
}

 

  • 변수명을 명시적으로 변경하자.
  • 필요 없는 제어 플래그를 제거하고, 바로 return 하자.
  • 향상된 for문을 사용하자.
Point[] point;

public boolean existPoint(int x, int y) {
    for (Point p : point) {
    	if (p.x == x && p.y == y) {
        	return true;
        }
    }

	return false;
}

 

728x90
반응형
LIST