본문 바로가기

Programming/리팩토링

[리팩토링 기술-7] 기본형을 객체로 바꾸기 / 타입 코드를 서브클래스로 바꾸기 / 조건부 로직을 다형성으로 바꾸기

👀 기본형 집착 ?

- 어플리케이션이 다루고 있는 도메인에 필요한 기본 타입을 만들지 않고

프로그래밍 언어가 제공하는 기본 타입을 사용하는 경우가 많다.

 

- 기본형으로는 단위 또는 표기법을 표현하기 어렵다.

 

 

 

🙌  기본형을 객체로 바꾸기 (Replace Primitive with Object)

 

- 개발 초기에 기본형 (숫자 또는 문자열)으로 표현한 데이터가

나중에는 해당 데이터와 관련있는 다양한 기능을 필요로 하는 경우가 많다. 

 

(예) 문자열로 표현하던 전화번호의 지역코드가 필요한 경우

(예) 숫자로 표현하던 온도의 단위(화씨, 섭씨)를 변환하는 경우

 

기본형을 사용한 데이터를 감싸 줄 클래스를 만들면, 필요한 기능을 추가할 수 있다.

 

[  Berfore  ]

 

  • Order 클래스에서 우선순위를 의미하는 prioirty 필드를 기본 자료형으로 사용하고 있으며

 

 

 

  • OrderProcessor 클래스 numberOfHighPriorityOrders() 함수는
  • Order 클래스를 순회하며 priority 가 high 또는 rush 인 객체를 찾아 개수를 count 한다.

 

 

 

  • 테스트코드

 

 

[  After 

 

- priority 필드는 다양한 기능을 필요로하기 때문에 단순 기본 자료형으로 사용하기보다는 객체로 사용하여야 한다.

 

- 우선순위는 String 형식이긴 하지만, 한 번 더 Priority 클래스로 감싸는 방향으로 리팩토링한다.

 

- 리팩토링 전에는 문자열에 해당하는 것이면 무엇이든 priority로 들어올 수 있기 때문에, type safety가 보장되지 않는다.

따라서 아래 8번째 줄 처럼 '허용 가능한 값'을 미리 선언하고,

생성자로 넘어온 값이 잘못되지 않았는지 확인한다. (11-15번째 줄) 

 

- higherThan() 메소드는 List의 인덱스 값을 통해 우선순위를 가려낼 수 있다.

 

 

 

 

기존에 있던 생성자 (10-12번쨰)를 그대로 활용하기 위해

체이닝을 통해 6-8번째 줄 처럼 연결할 수 있다. 

 

 

 

 


🙌  타입 코드를 서브클래스로 바꾸기  (Replace Type Code with Subclasses)

 

  • 비슷하지만 다른 것들을 표현해야 하는 경우, 문자열(String), 열거형 (enum), 숫자 (int) 등으로 표현하기도 한다.
    • 예) 주문 타입, “일반 주문”, “빠른 주문”
    • 예) 직원 타입, “엔지니어”, “매니저”, “세일즈”
  • 타입을 서브클래스로 바꾸는 계기
    • 조건문을 다형성으로 표현할 수 있을 때, 서브클래스를 만들고 “조건부 로직을 다형성으로 바꾸기”를 적용한다.
    • 특정 타입에만 유효한 필드가 있을 때, 서브클래스를 만들고 “필드 내리기”를 적용한다.

[  Berfore  ]

  • 리팩토링 이전에는 Employee 클래스에서 직원 타입을 분리하기 위해 내부 필드 type 의 문자열 값으로 구분하고 있다.

 

 

[  After 

  • 리팩토링 후에는 Employee 를 추상클래스로 수정
    • 서브 클래스에서 Employee를 상속받아
    • Employee 클래스에서 팩토리 패턴으로 타입에 해당하는 서브 클래스를 생성한다.
  • switch문으로 처리하기 때문에 validate() 메소드를 삭제했다.

 

 

 

 

 

 

 


🙌  조건부 로직을 다형성으로 바꾸기 (Replace Conditional with Polymorphism)  - 1

 

- 복잡한 조건식을 상속과 다형성을 사용해 코드를 보다 명확하게 분리할 수 있다.

 

- 기본 동작과 (타입에 따른) 특수한 기능이 섞여있는 경우에 상속 구조를 만들어서

기본 동작을 상위클래스에 두고 특수한 기능을 하위클래스로 옮겨서

각 타입에 따른 “차이점”을 강조할 수 있다.

 

- 단순한 조건문은 그대로 두어도 좋다.

오직 복잡한 조건문을 다형성을 활용해 좀 더 나은 코드로 만들 수 있는 경우에만 적용한다.

 

 

[  Berfore  ]

 

- type 이 full-time, part-time, temporal 인지에 따라 canAccessTo() 메소드의 결과 값이 달라지고,

vactionHours() 함수의 결과 값도 달라진다.

 

-  vacationHours(), canAccessTo() 메소드는 필드인 type 에 따라 각기 다른 동작을 수행한다. 

다형성을 활용하여 기본 동작을 상위클래스에 두고 특수한 동작을 하위클래스로 옮길 필요가 있다.

 

- 타입에 따른 Employee 클래스를 상속 받는 자식 클래스를 만드는 리팩토링을 할 수 있다.

 

 

 

 

[  After 

- Employee 를 추상클래스로 하여 기본동작은 Employee 클래스에서 수행하고,

FullTimeEmployee, PartTimeEmployee, TemporalEmployee 에서 각 타입별 특수한 동작을 수행한다.

 

 

 

 

 

 

 


🙌  조건부 로직을 다형성으로 바꾸기 (Replace Conditional with Polymorphism)  - 2

 

 

[  Berfore  ]

 

- 코드를 보면, 조건이 china인 경우는 특이케이스로 처리되는 것을 볼 수 있다.

 

 

 

 

[  After 

 

- china 관련된 코드를 분리하기 위해, 부가적인 구조로 옮겨갈 클래스 계층구조 (= RatingFactory)를 만든다.

 

 

 

- china와 관련된 코드는 ChinaExperiencedVoyageRating 클래스로 모은다.

 

 

 

 

- VoyageRating 클래스에서 ChinaExperiencedVoyageRating  클래스로 옮길 때 비교화면 

 

 

 

 

- VoyageRating에서 china와 관련된 코드들이 빠진 것을 볼 수 있다.

 

 

 

 

 

 

 

 

 

출처 : 인프런 강의 (백기선 - 코딩으로 학습하는 리팩토링)