티스토리 뷰

코드 리팩토링

Chapter2. Function (2)

lkh's 2020. 1. 27. 22:41

Clean Code Function에 대해 더 알아보려고 한다. 


Use Descriptive Names


메소드의 이름을 선택하는데 많은 시간을 소요하는 것을 두려워하지 마라! 가장 적합한 이름을 선택해라!

지금 개발하는 나의 시간이 조금 더 소요될 수는 있겠지만, 훗날 누군가 코드를 이해하는 시간을 많이 감소시킬 수 있을 것이다!


Function Arguments


이상적인 메소드의 파라미터수는 몇개일까?! 적을 수록 좋고, 없으면 더욱 좋다. 그리고 3개 이상부터는 권장하지 않으니 피해라!

메소드의 파라미터수가 많다는테스트 하기 어렵고, 결국은 사용하기도 어렵다는 것이다. 테스트 코드를 작성 할 때, 해당 메소드가 정상적으로

수행하는지 확인하기 위해 거의 모든 경우의 수를 테스트 할 것이다. 그런데 파라미터수가 많다는 것은 그만큼 테스트의 경우의 수도 증가하기 때문에

테스트하기 어렵다. 그리고 메소드의 파라미터수가 많아지면, 해당 메소드를 파악하는 것이 어렵다.


메소드의 파라미터가 하나 전달되는 경우

    • 전달되는 파라미터에 대해 질의하는 경우 (예 : boolean isExistFile("MyFile"))

    • 인자에 어떤 연산을 수행하고, 다른 어떤것으로 변환하고 반환하는 경우 (예 : InputStream fileOpen("MyFile"))

    • 메소드에 flag값을 통해 분기처리를 하는 경우 (예 : if(flag) { drawTemplate() } else { loadTemplate() }

      • 결국은 true, false를 통해 두가지 이상의 일을 하기 떄문에 권장하지 않는다. 메소드는 한가지 일만 해야한다! 라고 앞서 계속 말했다!

메소드의 파라미터가 세개 이상 전달되는 경우는 권장하지 않지만, 그런 상황이 발생할 수 있다.
그럴때는 파라미터를 포함하고 있는 클래스를 생성하는 것도 방법이다. 
    • Circle createCircle(double x., double y, double radius) → (X)
    • Circle createCircle(Point center, double radius) → (O) : 의도를 추측할 수 있는 이름을 가진 파라미터를 통해 더 명확해짐
의도를 추측할 수 있는 이름을 가진 파라미터를 통해 해당 메소드를 이해하는데 더욱 편해졌다.
그리고 다른 방법으로는 해당 클래스의 인스턴스 멤버로 가지는 것이다. Function (1)에 있는 코드를 보면 각 메소드들은 공통적으로 WikiPage, PageData를 
사용한다. 이런 공통적인 필드같은 겨우, 인스턴스 멤버로 가지면 중복적인 파라미터를 제거할 수 있고, 해당 필드들은 상태를 가지고 있어도 무관하기 때문에
인스턴스 필드로 분리해도 좋다.

Have no side effects

메소드가 한가지 일만한다고 해놓고 다른 숨겨진 일도 하는 경우가 있다. 예상치 못한 변경과 예상치 못한 의존성을 가지는 경우가 있다.
예를들어, 아래와 같은 메소드는 네이밍만 봤을 때, 비밀번호를 확인하여 일치유무를 반환하겠구나! 라고 생각할 수 있다.
그러나 코드를 보면 비밀번호가 맞으면 Session을 초기화하는 로직이 있다. 이름에서는 전혀 파악할 수 없고, 메소드를 분석해야만 알 수 있다.
이런 예상치 못한 변화가 Side Effect이며 피해야 한다. 이 또한 메소드는 한가지 일만 해야한다는 규칙을 어기는 셈이다.
이럴 경우, 이름을 checkPasswordAndInitializeSession 이라고 명시하는 것이 좋다. 
  
 public class UserValidator { 
  private Cryptographer cryptographer; 
  public boolean checkPassword(String userName, String password) { 
    User user = UserGateway .findByName(userName); 
    if (user != User .NULL) { 
      String codedPhrase = user.getPhraseEncodedByPassword(); 
      String phrase = cryptographer.decrypt(codedPhrase, password); 
      if ("Valid Password".equals(phrase)) { 
        Session .initialize(); 
        return true; 
      } 
    } 
    return false; 
  } 
 } 

Extract Try ~ Catch Blocks


try ~ catch 는 항상 코드를 지저분하게 보이게 하는 특별한 재능이 있는 구문이다. 그렇다고 절대 제거할 수 없다.

그리고 try ~ catch는 메소드는 하나의 일만 수행해야 한다는 규칙을 깨고 있는 구문이다. 왜냐면?! 정상처리와 예외처리라는 두가지 일을 수행하고 있기 

때문이다. 그렇기 떄문에 try 블럭과 catch 블럭을 별도로 각각 메소드로 분리하는 것이 좋다.


  
   public void delete (Page page) { 
    try  { 
      deletePageAndAllReferences(page); 
     } 
     catch (Exception e) { 
      logError(e); 
     } 
   } 

   private void deletePageAndAllReferences(Page page) throws Exception { 
    deletePage(page); 
    registry.deleteReference(page.name); 
    configKeys.deleteKey(page.name.makeKey()); 
   } 

   private void logError(Exception e) { 
    logger.log(e.getMessage()); 
   }

메소드 블럭 내의 추상화 단계를 모두 같게 해야하는 것 외 다른 내용들은 한번쯤은 들어봤던 내용이다.

하지만 잘 지키지 못했다. 시간이 부족해서 ... 기존에 이미 이렇게 되있어서 ... (깨진 유리창) 라는 핑계를 스스로 하면서 못 지킨거 같다..


'코드 리팩토링' 카테고리의 다른 글

Humble Object Pattern  (0) 2020.07.19
Chapter3. Claass  (0) 2020.02.08
Chapter2. Function (1)  (0) 2020.01.26
Chapter1. 의미있는 변수 사용  (0) 2020.01.08
생성자의 인자가 많을 때 빌더 패턴  (0) 2019.01.16
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함