코드의 구린내

niee
Written by niee on

  • 구린게 있으면 그 부분을 바로 잡으세요.
  • 리팩토링을 어떨 때 시작하고 어떨 때 그만둬야 할지 판단하는 일은 리팩토링 기법을 적용하는 방법만큼 중요하다.

1. 중복코드

  • 구린내의 제왕 중복코드.
  • 똑같은 코드 구조가 두 군데 이상 있을 때는 그 부분을 하나로 통일하면 프로그램이 개선
  • 한 클래스의 두 메서드 안에 같은 코드가 들어있는경우 -> 메서드 추출 기법을 적용해서 겹치는 코드를 빼내어 별도의 메서드로 만들고 그 메서 드를 두 곳에서 호출
  • 수퍼클래스의 두 하위 클래스에 같은 코드가 들어 있는 경우 -> 메서드 추출 기법을 적용해서 중복을 없앤 후 메서드 상향 기법을 적용하면 된다.
  • 위 상황에 코드가 똑같지 않고 비슷하다면 메서드 추출기법을 적용해서 같은 부분과 다른 부분을 분리해야 한다. 그런 다음 경우에 따라 템플릿 메서드 형성 기법을 적용해야 할 수도있다.
  • 두 메서드가 알고리즘만 다르고 기능이 같다면 개발자는 그 두 알고리즘 중에서 더 간단한 것을 택해서 알고리즘 전환 기법을 적용하면 된다.
  • 중복 코드가 메서드 가운데에 있다면 주변 메서드 추출을 적용하면된다.
  • 서로 상관 없는 두 클래스 안에 중복코드가 있을 때 -> 한 클래스 안의 중복 코드를 클래스 추출 이나 모듈 추출 을 적용해 제 3의 클래스나 모듈로 떼어낸 후 그것을 다른 클래스에서 호출하는 방법이 있다. 또는 중복 코드를 빼서 메서드로 만든 후 그 메서드를 두 클래스 중 하나에 넣고 다른클래스에서 그 메서드를 호출하거나 코드를 빼내어 만든 메서드를 제 3의 클래스에 넣고 그걸 두 클래스에서 호출하는 방법이 있다

2. 장황한 메서드

  • 최적의 상태로 장수하는 객체 프로그램을 보면 공통적으로 메서드 길이가 짧다.
  • 짧은 메서드를 이해하기 쉽게하려면 메서드 명을 잘 정해야한다. 메섣의 기능을 한눈에 알 수 있는 메서드명을 사용하면 그 메서드안의 코드를 분석하지 않아도된다.
  • 메서드 호출이 원래 코드보다 길어지는 한이 있어도, 메서드 명은 그 코드의 의도를 잘 반영하는 것으로 정해야한다.
  • 메서드명은 기능 수행 방식이 아니라 목적(즉, 기능자체)을 나타내는 이름으로 정한다.
  • 메서드 추출기법을 적용한다.
  • 코드를 여러 덩어리로 분리하려면?
    • 주석을 찾는 것 -> 기능 설명이 주석으로 처리된 코드 구간을 메서드로 만들면 된다. (긴 메서드에서 기능 설명이 주석으로 되어있는 부분)
    • 조건문과 루프도 역시 메서드로 추출

3. 방대한 클래스

  • 기능이 지나치게 많은 클래스에는 보통 엄청난 수의 인스턴스 변수가 들어있다. 클래스에 인스턴스 변수가 너무 많으면 중복 코드가 반드시 존재하게 마련

4. 과다한 매개변수

  • 매개변수 세트가 길면 서로 일관성이 없어지거나 사용이 불편, 더많은 데이터가 필요해질 때마다 계속 수정해야 한다
  • 즉 매개변수는 객체를 넘기도록 한다. 객체를 넘김으로써 위 문제들이 해결

5. 수정의 산발

  • 수정의 산발은 한 클래스가 다양한 원인 때문에 다양한 방식으로 자주 수정될 때 일어난다.
  • 하나의 클래스를 여러 개의 변형 객체로 분리하는것이 좋다. 그러면 각 객체는 한 종류의 수정에 의해서만 변경된다.

6. 기능의 산재

  • 하나의 수정으로 여러 클래스가 바뀌게 되는 문제
  • 수정할 부분들을 전부 하나의 클래스 안에 넣어줘야 한다.

7 .잘못된 소속

  • 메서드가 자신이 속해있는 클래스보다 다른 클래스에서 더 호출이 이루어질 경우 해당 메서드를 더 접근이 많은 클래스로 옮겨줘야 한다.

8. 데이터 뭉치

  • 두 클래스에 들어 있는 인스턴스 변수나 여러 메서드 시그너처(메서드명과 인수들 목록을 메서드 시그너처 라고 부릅니다.)에 들어있는 매개변수 처럼, 동일한 3~4개의 데이터 항목이 여러 위치에 몰려있는 경우
  • 이렇게 몰려있는 데이터 뭉치는 객체로 만들어야한다.

9. 강박적 기본 타입 사용

  • 관련된 데이터를 묶지 못하고 흩어놓게 되면, 각각의 데이터에 대한 정보를 외부에 공개해야한다.
  • 함수를 만들때도 각각의 데이터를 파라미터로 넘겨주어야 하기에 파라미터의 갯수가 늘어나게 된다.
  • 기본형만 사용할 바에는 객체를 만들어서(구조화)해서 사용해라
  • 이때는 각각의 관련된 데이터를 하나의 구조체로 묶어 주어야한다.

10. switch 문

  • switch 문의 단점은 반드시 중복이 생긴다는 점이다. 동일한 switch가 다른 곳에서 또 쓰일가능성이 크다
  • switch 문에 새 코드행을 추가하려면 그렇게 여기저기에 존재하는 switch 문을 전부 찾아서 수정해야한다.
  • 이 문제를 해결할수 있는 방법은 다형성 즉 재정의를 이용하는 것이다.
  • switch 문을 메서드 추출로 빼낸 후 메서드 이동을 실시해서 그 메서드를 재정의해야 할 클래스에 옮겨 넣으면 된다.

11. 평행 상속 계층

  • 한 클래스의 하위클래스를 만들 때마다 매번 다른 클래스의 하위 클래스도 만들어야 한다.
  • 중복 코드 부분을 제거하려면 보통은 한 상속 계층의 인스턴스가 다른 상속계층의 인스턴스를 참조하게 만들면 된다.

12. 직무유기 클래스

  • 하나의 클래스를 작성할 때마다 유지관리와 이해하기 위한 비용이 추가된다.
  • 비용만큼의 기능을 수행하지 못하는 비효율적인 클래스는 삭제해야한다.

13. 막연한 범용 코드

  • 메서드나 클래스가 오직 테스트 케이스에만 사용된다면 구린내를 풍기는 유력한 용의자로 막연한 범용코드를 지목할 수 있다. 이러한 메서드나 클래스를 발견하면 그것과 그것을 실행하는 테스트 케이스는 삭제하자.

14. 임시 필드

  • 객체 안에 인스턴스 변수가 특정 상황에서만 할당되는 경우가 간혹 있다.
  • 이러한 떠돌이 해당 변수들을 사용하는 class를 생성한다

15. 메시지 체인

  • 메시지 체인?
    • 클라이언트가 한 객체에 제 2의 객체를 요청하면, 제 2의 객체가 제 3의 객체를 요청하고 …. 연쇄적으로 요청이 발생하는 문제점

16. 과잉 중개 메서드

  • 어떤 클래스의 인터페이스를 보니 안의 절반도 넘는 메서드가 기능을 다른 클래스에 위임하고 있을경우

17. 지나친 관여

  • 클래스 끼리 관계가 지나치게 밀접한 나머지 서로의 private를 알아내느라 과도한 시간낭비
  • 서로 지나지게 친밀한 클래스는 메서드 이동과 필드 이동으로 떼어 낸다.

18. 인터페이스가 다른 대용 클래스

  • 기능은 같은데 시그너처가 다른 메서드에는 메서드명 변경을 실시해야 한다.

19. 미흡한 라이브러리 클래스

  • 라이브러리 클래스에 넣어야 할 메서드가 두 개 뿐이라면 외래 클래스에 메서드 추가 기법을, 부가 기능이 많을 때는 국소적 상속확장 클래스 사용 기 법을 사용

20. 데이터 클래스

  • 데이터 클래스(domain)는 필드 캡슐화기법을 실시해야한다
  • 변경되지 않아야 하는 필드에는 쓰기 메서드 제거를 적용

21. 방치된 상속물

  • 하위클래스가 부모클래스에게 상속받은 메서드나 데이터가 하위클래스에서 더이상 쓰이지 않거나 필요 없게 되었을때.
  • 위 문제의 원인은 잘못된 계층구조
  • 새 대등 클래스를 작서하고 메서드 하향과 필드하향을 실시해서 사용되지 않는 모든 메서드를 그 형제 클래스에 몰아 넣는다.

22. 불필요한 주석

  • 주석을 넣어야겠다는 생각이 들 땐 먼저 코드를 리팩토링해서 주석을 없앨 수 있게 만들어보자.
  • 주석은 무슨 작업을 해야 좋을지 모를 때만 넣는 것이 좋다.
  • 어떤 코드를 넣은 이유를 메모해 놓을 경우에도 주석을 넣는 것이 적절하다.

Comments

comments powered by Disqus