티스토리 뷰

책/클린코드

클린코드 14장

먹태 2021. 12. 28. 22:27

14장. 점진적인 개선

Args 클래스를 소개하며 초창기의 소스와 비교하며 점진적으로 개선되는 모습을 보여준다.

소스를 그냥 냅다 붙여넣기 해버려서 진짜 읽기 싫게 해놨다.

그냥 제일 앞에 잘 짜여졌다는 소스라도 따라 쳐보면서 이해하는게 빠르다.

 

 

Args 구현

public static void main(String[] args) {
    try {
        Args arg = new Args(“l,p#,d*”, args);
        boolean logging = arg.getBoolean(‘l’);
        int port = arg.getInt(‘p’);
        String directory = arg.getString(‘d’);
        executeAppliocation(logging, port, directory);
    } catch (ArgsException e) {
        System.out.printf(“Argument error: %s\n”, e.errorMessage());
        }
}

- 매개변수 두 개로  Args 클래스의 인스턴스를 만들었다

첫째 매개변수는 형식 또는 스키마를 지정한다 (-l: 부울, -p 정수, -d문자열)

Args로 생성자로 넘긴 둘째 매개변수는 main으로 넘어온 명령행 인수 배열 자체다

 

생성자에서 argsException이 발생하지 않는다면 명령행 인수의 구문을 성공적으로 분석했으며

Args인스턴스에 질의를 던져도 좋다는 말이다

(인수값을 가져오려면 getBoolean, getInteger ,GetString과 같은 메서드를 사용)

 

형식 문자열이나 명령행 인수 자체에 문자가 있다면 ArgsException이 발생

(구체적인 오류를 알아내려면 errorMessage 메서드 이용)

 

 

Args.java

package args;

import java.util.*;


public class Args {
    private Map<Character, ArgumentMarshaler> marshalers;
    private Set<Character> argsFound;
    private ListIterator<String> currentArgument;

    public Args(String schema, String[] args) throws ArgsException {
        marshalers = new HashMap<Character, ArgumentMarshaler>();
        argsFound = new HashSet<Character>();

        parseSchema(schema);
        parseArgumentStrings(Arrays.asList(args));
    }

    private void parseSchema(String schema) throws ArgsException {
        for (String element : schema.split(","))
            if (element.length() > 0)
                parseSchemaElement(element.trim());
    }

    private void parseSchemaElement(String element) throws ArgsException {
        char elementId = element.charAt(0);


        String elementTail = element.substring(1); validateSchemaElementId(elementId);
        if (elementTail.length() == 0)
            marshalers.put(elementId, new BooleanArgumentMarshaler());
        else if (elementTail.equals("*"))
            marshalers.put(elementId, new StringArgumentMarshaler());
        else if (elementTail.equals("#"))
            marshalers.put(elementId, new IntegerArgumentMarshaler());
        else if (elementTail.equals("##"))
            marshalers.put(elementId, new DoubleArgumentMarshaler());
        else if (elementTail.equals("[*]"))
            marshalers.put(elementId, new StringArrayArgumentMarshaler());
        else
            throw new ArgsException(ArgsException.ErrorCode.INVALID_ARGUMENT_FORMAT, elementId, elementTail);
    }

    private void validateSchemaElementId(char elementId) throws ArgsException {
        if (!Character.isLetter(elementId))
            throw new ArgsException(ArgsException.ErrorCode.INVALID_ARGUMENT_NAME, elementId, null);
    }

    private void parseArgumentStrings(List<String> argsList) throws ArgsException {
        for (currentArgument = argsList.listIterator(); currentArgument.hasNext();) {
            String argString = currentArgument.next();
            if (argString.startsWith("-")) {
                parseArgumentCharacters(argString.substring(1));
            } else {
                currentArgument.previous();
                break;
            }
        }
    }

    private void parseArgumentCharacters(String argChars) throws ArgsException {
        for (int i = 0; i < argChars.length(); i++)
            parseArgumentCharacter(argChars.charAt(i));
    }

    private void parseArgumentCharacter(char argChar) throws ArgsException {
        ArgumentMarshaler m = marshalers.get(argChar);
        if (m == null) {
            throw new ArgsException(ArgsException.ErrorCode.UNEXPECTED_ARGUMENT, argChar, null);
        } else {
            argsFound.add(argChar);
            try {
                m.set(currentArgument);
            } catch (ArgsException e) {
                e.setErrorArgumentId(argChar);
                throw e;
            }
        }
    }

    public boolean has(char arg) {
        return argsFound.contains(arg);
    }

    public int nextArgument() {
        return currentArgument.nextIndex();
    }

    public boolean getBoolean(char arg) {
        return BooleanArgumentMarshaler.getValue(marshalers.get(arg));
    }

    public String getString(char arg) {
        return StringArgumentMarshaler.getValue(marshalers.get(arg));
    }

    public int getInt(char arg) {
        return IntegerArgumentMarshaler.getValue(marshalers.get(arg));
    }

  	public double getDouble(char arg) {
        return DoubleArgumentMarshaler.getValue(marshalers.get(arg));
    }

    public String[] getStringArray(char arg) {
        return StringArrayArgumentMarshaler.getValue(marshalers.get(arg));
    }
}

 

- 위 코드는 여기저기 뒤적일 필요 없이 위에서 아래로 코드가 읽히며 전반적으로 깔끔한 구조에 잘 짜인 프로그램으로 여겨진다.

- 해당 코드는 처음부터 이렇게 구현되지 않았다. 깨끗한 코드를 짜려면 먼저 지저분한 코드를 짠 뒤에 정리해야한다

- 코드는 언제나 최대한 깔끔하고 단순하게 정리하자. 절대로 썩어가게 방치하면 안된다

 

 

 

 

*참고*

ArgsException.java

package args;

public class ArgsException extends Exception {
    private char errorArgumentId = '\0';
    private String errorParameter = null;
    private ErrorCode errorCode = ErrorCode.OK;

    public ArgsException() {}

    public ArgsException(String message) {super(message);}

    public ArgsException(ErrorCode errorCode) {
        this.errorCode = errorCode;
    }
    public ArgsException(ErrorCode errorCode, String errorParameter) {
        this.errorCode = errorCode;
        this.errorParameter = errorParameter;
    }

    public ArgsException(ErrorCode errorCode, char errorArgumentId,
                         String errorParameter) {
        this.errorCode = errorCode;
        this.errorParameter = errorParameter;
        this.errorArgumentId = errorArgumentId;
    }

    public char getErrorArgumentId() {
        return errorArgumentId;
    }

    public void setErrorArgumentId(char errorArgumentId) {
        this.errorArgumentId = errorArgumentId;
    }

    public String getErrorParameter() {
        return errorParameter;
    }

    public void setErrorParameter(String errorParameter) {
        this.errorParameter = errorParameter;
    }

    public ErrorCode getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(ErrorCode errorCode) {
        this.errorCode = errorCode;
    }

    public String errorMessage() throws Exception {
        switch (errorCode) {
            case OK:
                throw new Exception("TILT: Should not get here.");
            case UNEXPECTED_ARGUMENT:
                return String.format("Argument -%c unexpected.", errorArgumentId);
            case MISSING_STRING:
                return String.format("Could not find string parameter for -%c.",
                        errorArgumentId);
            case INVALID_INTEGER:
                return String.format("Argument -%c expects an integer but was '%s'.",
                        errorArgumentId, errorParameter);
            case MISSING_INTEGER:
                return String.format("Could not find integer parameter for -%c.",
                        errorArgumentId);
            case INVALID_DOUBLE:
                return String.format("Argument -%c expects a double but was '%s'.",
                        errorArgumentId, errorParameter);
            case MISSING_DOUBLE:
                return String.format("Could not find double parameter for -%c.",
                        errorArgumentId);
        }
        return "";
    }

    public enum ErrorCode {
        OK, INVALID_ARGUMENT_FORMAT, UNEXPECTED_ARGUMENT,
        INVALID_ARGUMENT_NAME,
        MISSING_STRING,
        MISSING_INTEGER, INVALID_INTEGER,
        MISSING_DOUBLE, INVALID_DOUBLE}
}

- ArgsException을 독자적인 모듈로 뺌

 

 

ArgumentMarshaler.java

package args;

import java.util.Iterator;

public interface ArgumentMarshaler {
    void set(Iterator<String> currentArgument) throws ArgsException;
}

 

 

' > 클린코드' 카테고리의 다른 글

클린코드 15장, 16장, 17장  (0) 2021.12.29
클린코드 13장  (0) 2021.12.27
클린코드 10~12장  (0) 2021.12.24
클린코드 7~9장  (0) 2021.12.23
클린코드 4장 - 주석  (0) 2021.07.22
댓글
최근에 올라온 글
최근에 달린 댓글
링크
Total
Today
Yesterday