단어 뒤집기

김주형·2023년 8월 31일
0

알고리즘

목록 보기
17/29
post-thumbnail

첫 풀이

스택을 활용하여 해결해보려는 시도

import java.util.*;
  
public class Main {
  public static void main(String[] args){
    
    Scanner userInteface = new Scanner(System.in);
    
    int number = userInteface.nextInt();
    
    for (int i = 0; i < number; i++) {
      String nextLine = userInteface.next();
      System.out.println(reverse(nextLine));
    }
    
    return ;
  }
  
  // 단어 뒤집기 기능
  private static String reverse(String nextLine) {
    
    Stack<Character> stack = new Stack<>();
    StringBuilder reverse = new StringBuilder();
    
    // 가독성(for-each) vs 메모리 비용(for(int i..) 선택해야함
    for(char index: nextLine.toCharArray()){
      stack.push(index);
    }
    
    while(!stack.isEmpty()) {
      reverse.append(stack.pop());
    }
    
    return reverse.toString();
  }
}
  • 가독성(for-each) vs 메모리 비용(for(int i..) 선택해야하는 고민을 통해 클린 코드와 최적화 코드 중 어떤 코드가 더 나은 코드이고, 어떤 비즈니스 경쟁력을 갖추는지 고민하게 되었음
  • 결론적으로 하드웨어와 가깝거나 게임 개발 같은 빠르고 풍부한 실시간 사용자경험이 필요한 경우는 최적화 코드 (메모리 비용)
  • 디버깅과 유지보수, 복잡한 상황에 유연한 대응이 필요한 경우의 비즈니스에는 클린코드가 적절하지 않을까라고 생각함

코드 100줄 짜기

하나의 역할만 하도록

  • 메서드마다 여러 기능들이 복잡하게 얽혀있는게 싫었다
  • 각각의 기능들이 자신의 역할만을 수행하는게 분명해지도록 적절히 분리해주려 노력했다
import java.util.*;
  
public class Main {
  
  private Scanner userInterface;
  private static int number;
  
  public Main() {
    this.userInterface = new Scanner(System.in);
    this.number = userInteface.nextInt();
  }
  
  public static void main(String[] args){
    
    run();
    
    return ;
  }
  
  // 실행
  private static void run(){
    for (int i = 0; i < number; i++) {
      String nextLine = userInteface.next();
      System.out.println(reverse(nextLine));
    }
  }
  
  // 단어 뒤집기 기능
  private static String reverse(String nextLine) {
    
    Stack<Character> stack = new Stack<>();
    
    for(char index: nextLine.toCharArray()){
      stack.push(index);
    }
    
    return builder(stack).toString();
  }
  
  // 빌더 (각 메서드가 하나의 기능만을 책임지도록 하기 위해)
  private static StrungBuilder builder(Stack<Character> stack) {
    StringBuilder reverse = new StringBuilder();
    
    while(!stack.isEmpty()) {
      reverse.append(stack.pop());
    }
    
    return reverse;
  }
  
  
}

scanner를 일급 객체로

  • 일급 객체가 활용되는 장점을 체험하고 싶었다.
  • 메인 함수 때문에 static 체이닝 되는 현상을 main 인스턴스 생성으로 예방 가능하단 것을 알게 되었다.
import java.util.*;

public class Main {

    private UserInterface userInterface;
    
    public Main() {
        this.userInterface = new UserInterface();
    }

    public static void main(String[] args){
        Main main = new Main();
        main.run();
    }

    // 실행
    private void run() {
        int number = userInterface.getNumber();
        for (int i = 0; i < number; i++) {
            String nextLine = userInterface.getNextLine();
            System.out.println(reverse(nextLine));
        }
    }

    // 단어 뒤집기 기능
    private String reverse(String nextLine) {
        Stack<Character> stack = new Stack<>();
        
        for(char index: nextLine.toCharArray()) {
            stack.push(index);
        }
        
        return builder(stack).toString();
    }

    // 빌더 (각 메서드가 하나의 기능만을 책임지도록 하기 위해)
    private StringBuilder builder(Stack<Character> stack) {
        StringBuilder reverse = new StringBuilder();
        
        while(!stack.isEmpty()) {
            reverse.append(stack.pop());
        }
        
        return reverse;
    }

    // 스캐너를 일급 객체로
    private class UserInterface {
        private Scanner userInterface = new Scanner(System.in);

        public int getNumber() {
            return userInterface.nextInt();
        }

        public String getNextLine() {
            return userInterface.next();
        }
    }
}

예외 처리 추가

  • 사용자 경험을 개선하고 싶었음
  • 무수한 경우의 수의 사용자 입력이 발생할텐데,
  • 유효성 검사를 통과하지 못할 경우 프로그램이 종료되는 것이 아닌
  • 올바른 사용자 경험으로 고객을 인도할 수 있도록 로직을 짜고싶었음
  • 잘못 입력하면 제대로 입력할 때까지 반복

import java.util.*;

public class Main {

    private UserInterface userInterface;
    
    public Main() {
        this.userInterface = new UserInterface();
    }

    public static void main(String[] args){
        Main main = new Main();
        main.run();
    }

    // 실행
    private void run() {
        int number = userInterface.getNumber();
        for (int i = 0; i < number; i++) {
            String nextLine = userInterface.getNextLine();
            System.out.println(reverse(nextLine));
        }
    }

    // 단어 뒤집기 기능
    private String reverse(String nextLine) {
        Stack<Character> stack = new Stack<>();
        
        for(char index: nextLine.toCharArray()) {
            stack.push(index);
        }
        
        return builder(stack).toString();
    }

    // 빌더 (각 메서드가 하나의 기능만을 책임지도록 하기 위해)
    private StringBuilder builder(Stack<Character> stack) {
        StringBuilder reverse = new StringBuilder();
        
        while(!stack.isEmpty()) {
            reverse.append(stack.pop());
        }
        
        return reverse;
    }
  
    // Scanner를 일급 객체로
    private class UserInterface {
        private Scanner userInterface = new Scanner(System.in);

        public int getNumber() {
          int number = 0;
          
          while(true) {
            try {
              number = userInterface.nextInt();
              validateRange(number);
              break;
            
            } catch (InputMismatchException e) {
              System.out.println("[ERROR] 올바른 숫자를 입력해주세요 (범위: 3 ~ 20)");
              userInterface.nextLine(); // 잘못된 입력 버퍼 비움
            } 
           return number;
          }
          
          return number;
            
        }

        public String getNextLine() {
          String nextLine = "";
          
          while(true) {
            try {
            
              nextLine = userInterface.next();
              validateException(nextLine);
              break;
              
            } catch (IllegalArgumentException e) {
              System.out.println("잘못된 값을 입력했습니다.");
              userInterface.next(); // 잘못된 입력 버퍼 비움
            } 
          }
          
          return nextLine;
        }
      
      
        // 유효성 체크
        private boolean validateRange(int number) {
          return number >= 3 && number <= 20;
          
        }
      
        private boolean validateException(String nextLine) {
          return nextLine.matches("\\d+");
        }
    }
}

완벽한 목수

  • 훌륭한 목수는 보이지 않는 뒷편까지 완벽한 나무를 사용하여 가구를 제작한다
  • 모든 코드의 depth를 1이하로 줄이면서
  • 동시에 정상 동작하는 코드로 개선하고 싶었다
  • 컴파일 에러를 해결하지 못했다 빌어먹을
  • 다시 태어난다면 꼭 더 완벽하게 만들고말테다
profile
근면성실

0개의 댓글