[클린코드 완독스터디] TIL (2022.02.20)

yourjin·2022년 2월 27일
0

read.log

목록 보기
30/37
post-thumbnail

TIL (2022.02.20)

DAY 7

🔖 오늘 읽은 범위 : 14장, 점진적인 개선


😃 책에서 기억하고 싶은 내용을 써보세요.

  • 전체 소스 코드는 여기서 확인해주세요!
    • Args.java
      package com.objectmentor.utilities.args;
      
      import static com.objectmentor.utilities.args.ArgsException.ErrorCode.*;
      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 BooleanArgumentMarshaled()) ;
      				else if(elementTail.equals("*"))
      						marshalers.put(elementId, new StringArgumentMarshaled()) ;
      				else if(elementTail.equals("#"))
      						marshalers.put(elementId, new IntegerArgumentMarshaled()) ;
      				else if(elementTail.equals("##"))
      						marshalers.put(elementId, new DoubleArgumentMarshaled()) ;
      				else if(elementTail.equals("[*]"))
      						marshalers.put(elementId, new StringArrayArgumentMarshaled()) ;
      				else
      						throw new ArgsException(INVALID_ARGUMENT_FORMAT, elementId, null);
      		}
      
      		private void validateSchemaElementId(char elementId) throws ArgsException {	
      				if(!Character.isLetter(elementId)) 
      					throw new ArgsException(INVALID_ARGLMENT_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(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)) ;
      		}
      }
    • ArgumentMarshaler.java
      public interface ArgumentMarshaler {
      		void set(Iterator<String> currentArgument) throws ArgsException;
      }
    • BooleanArgumentMarshaler.java
      public class BooleanArgumentMarshaler implements ArgumentMarshaler {
      		private boolean booleanValue = false;
      
      		public void set(Iterator<String> currentArgument) throws ArgsException {
      				booleanValue = true;
      		}
      
      		public static boolean getValue(ArgumentMarshaler am) {
      				if(am != null && am instanceof BooleanArgumentMarshaler)
      						return ((BooleanArgumentMarshaler) am).booleanValue;
      				else
      						return false ;
      		}
      }
    • StringArgumentMarshaler.java
      import static com.objectmentor.utilities.args.ArgsException.ErrorCode. *;
      
      public class StringArgumentMarshaler implements ArgumentMarshaler {
      		private String stringValue = "";
      
      		public void set(Iterator<String> currentArgument) throws ArgsException {
      				try {
      						stringValue = currentArgument.next();
      				} catch(NoSuchElementException e) {
      						throw new ArgsException(MISSING_STRING);
      				}
      		}
      
      		public static String getValue(ArgumentMarshaler am) {
      				if(am != null && am instanceof StringArgumentMarshaler)
      						return ((StringArgumentMarshaler) am).stringValue;
      				else
      						return "";
      		}
      }
    • IntegerArgumentMarshaler.java
      import static com.objectmentor.utilities.args.ArgsException.ErrorCode. *;
      
      public class IntegerArgumentMarshaler implements ArgumentMarshaler {
      		private int intValue = 0;
      
      		public void set(Iterator<String> currentArgument) throws ArgsException {
      				String parameter = null;
      				try {
      						parameter = currentArgument.next();
      						intValue = Integer.paresInt(paramenter);
      				} catch(NoSuchElementException e) {
      						throw new ArgsException(MISSING_INTEGER);
      				} catch(NumberFormatException e)){
      						throw new ArgsException(INVALID_INTEGER, parameter);
      				}
      		}
      
      		public static int getValue(ArgumentMarshaler am) {
      				if (am != null && am instanceof IntegerArgumentMarshaler)
      						return ((IntegerArgumentMarshaler) am).intValue;
      				else
      						return 0;
      		}
      }
    • ArgsException.java
      import static com.objectmentor.utilities.args.ArgsException.ErrorCode.*;
      
      public class ArgsException extends Exception {
      		Private char errorArgumentid = '\0' ;
      		Private String errorParameter = null;
      		Private 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 setErrorArgumentld(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( ) {
      				switch(errorCode) {
      						case OK:
      								return "TILT: Should not get here.";
      						case UNEXPECTED_ARGUMENT:
      								return String.format("Argument -%c unexpected.", errorArgumentId) ;
      						case MISSING_STRING:
      								return 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) ;
      						case INVALID_ARGUMENT_NAME:
      								return String.format("'%c' is not a valid argument name.", errorArgumentId) ;
      						case INVALID_ARGUMENT_FORMAT: 
      								return String.format("'%s' is not a valid argument format.", errorParameter) ;
      				}
      				return "";
      		}
      
      		public enum ErrorCode {
      				OK, INVALID_ARGLMENT_FOOOT, LNEXPECTED_ARGLMENT,
      				INVALID_ARGLMENT_NAME ,
      				MISSING_STRING ,
      				MISSING_INTEGER, INVALID_INTEGER,
      				MISSING_DOUBLE , INVALID_DOUBLE }
      }
  • 우선, 출발은 좋았으나 확장성이 부족했던 모듈을 소개한다. 그런 다음, 모듈을 개선하고 정리하는 단계를 살펴본다.
  • 새로 짤 유틸리티를 Args라 부르겠다. Args는 사용법이 간단하다. Args 생성자에 (입력으로 들어온) 인수의 문자열과 형식 문자열을 넘겨 Args 인스턴스를 생성한 후 Args 인스턴스에다 인수 값을 질의한다.
  • Args 구현
    • 여기저기 뒤적일 필요 없이 위에서 아래로 코드가 읽힌다는 사실에 주목한다.
    • 한 가지가 눈에 거슬릴지 모르겠다. 바로 오류 코드 상수를 정의하는 부분이다. (…) 이처럼 단순한 개념을 구현하는데 코드가 너무 많이 필요해 놀랄지도 모르겠다. 한 가지 이유는 우리가 장황한 언어인 자바를 사용하는 탓이다. 자바는 정적 타입 언어라서 타입 시스템을 만족하려면 많은 단어가 필요하다.
    • 노련한 프로그래머라면 여기저기 자질한 구조나 스타일이 거슬릴지 모르겠지만 전반적으로 깔끔한 구조에 잘 짜인 프로그램으로 여겨주면 좋겠다.
    • 예를 들어, 날짜 인수나 복소수 인수 등 새로운 인수 유형을 추가하는 방법이 명백하다. 고칠 코드도 별로 없다.
    • 어떻게 짰느냐고?
      • 지난 수십 여년 동안 쌓아온 경험에서 얻은 교훈이라면, 프로그래밍은 과학보다 공예(craft)에 가깝다는 사실이다. 깨끗한 코드를 짜려면 먼저 지저분한 코드를 짠 뒤에 정리해야 한다는 의미다.
      • 대다수 신참 프로그래머는 (대다수 초딩과 마찬가지로) 이 충고를 충실히 따르지 않는다. 그들은 무조건 돌아가는 프로그램을 목표로 잡는다. 일단 프로그램이 ‘돌아가면’ 다음 업무로 넘어간다.
      • 경험이 풍부한 전문 프로그래머라면 이런 행동이 전문가로서 자살 행위라는 사실을 잘 안다.

🤔 오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요

  • 오늘은 밥 아저씨가 작성한(?) 깔끔한 코드를 볼 수 있는 좋은 기회였다. 이렇게 긴 코드를 읽었는데도 딱히 눈살이 찌뿌려지지 않은걸 보면 꽤나 이해하기 좋은 코드였음이 분명하다. 처음의 코드는 어땠을지, 몇 번의 수정을 거쳐 이 코드가 나올 수 있었는지 궁금해졌다.

🔎 궁금한 내용이 있거나, 잘 이해되지 않는 내용이 있다면 적어보세요.

소감 3줄 요약

  • 여기저기 뒤적일 필요 없이 코드가 위에서 아래로 읽힌다면 잘 짠 코드이다.
  • 새로운 인수 유형을 추가하는 게 명백하다면, 확장성이 높은 코드이다.
  • 깨끗한 코드를 짜려면 먼저 지저분한 코드를 짠 뒤에 정리해야 한다.
profile
make it mine, make it yours

0개의 댓글