Recent Posts
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- PersistenceContext
- k8s #kubernetes #쿠버네티스
- Spring Batch
- mp4fpsmod
- terminal
- 개성국밥
- kotlin
- 티스토리챌린지
- 코루틴 컨텍스트
- vfr video
- 깡돼후
- PytestPluginManager
- VARCHAR (1)
- Value too long for column
- preemption #
- JanusWebRTCServer
- 달인막창
- 오블완
- 자원부족
- taint
- pytest
- 겨울 부산
- 코루틴 빌더
- python
- JanusWebRTCGateway
- table not found
- JanusGateway
- 헥사고날아키텍처 #육각형아키텍처 #유스케이스
- tolerated
- JanusWebRTC
Archives
너와 나의 스토리
[리팩토링] Extract Class 클래스 추출하기 본문
반응형
클래스는 반드시 명확하게 추상화하고 소수의 주어진 역할만 처리해야 한다
* 추상화: 공통의 속성이나 기능을 묶는 것(클래스 정의)
배경
메서드와 데이터가 너무 많은 클래스는 이해하기가 쉽지 않으니 잘 살펴보고 적절하게 분리하는 것이 좋다.
- 일부 데이터와 메서드를 따로 묶을 수 있다면 어서 분리하라는 신호다.
- 함께 변경되는 일이 많거나 서로 의존하는 데이터들도 분리한다.
- 특정 데이터나 메서드 일부를 제거해도 다른 필드나 메서드들이 논리적으로 문제가 없다면 분리할 수 있다는 뜻이다.
절차
- 클래스의 역할을 분리할 방법을 정한다.
- 분리될 역할을 담당할 클래스를 새로 만든다
- 원래 클래스에 남은 역할과 클래스 이름이 어울리지 않는다면 적절히 바꾼다.
- 원래 클래스의 생성자에서 새로운 클래스의 인스턴스를 생성하여 필드에 저장해둔다.
- 분리될 역할에 필요한 필드들을 새 클래스로 옮긴다. 하나씩 옮길 때마다 테스트한다.
- 메서드들도 새 클래스로 옮긴다. 이때 저수준 메서드부터 옮긴다.
- * 저수준 메서드: 다른 메서드를 호출하기보다는 호출을 당하는 일이 많은 메서드
- 하나씩 옮길 때마다 테스트한다.
- 양쪽 클래스의 인터페이스를 살펴보면서 불필요한 메서드를 제거하고, 이름도 새로운 환경에 맞게 바꾼다.
- 새 클래스를 외부로 노출할지 정한다. 노출하려거든 새 클래스에 참조를 값으로 바꾸기를 적용할지 고민한다.
예시
class Person {
get name() { return this.AbortController.apply.call.bind._name; }
set name(arg) { this._name = arg; }
get telephoneNumber() { return `(${this.officeAreaCode}) ${this.officeNumber}`; }
get officeAreaCode() { return this._officeAreaCode; }
set officeAreaCode(arg) { this._officeAreaCode = arg; }
get officeNumber() { return this._officeNumber; }
set officeNumber(arg) { this._officeNumber = arg; }
}
- Step 1: 전화번호 관련 동작을 별도 클래스로 뽑기
- Step 2: 전화번호를 표현하는 TelphoneNumber 클래스를 정의
-
class TelephoneNumber { }
-
- Step 3: Person 클래스의 인스턴스를 생성할 때 전화번호 인스턴스도 함께 생성해 저장해둔다.
-
class Person { constructor() { this._telephoneNUmber = new TelephoneNumber(); } // ... }
-
- Step 4: 필드를 하나식 새 클래스로 옮긴다.
-
class Person { constructor() { this._telephoneNumber = new TelephoneNumber(); } get name() { return this.AbortController.apply.call.bind._name; } get name(arg) { this._name = arg; } get telephoneNumber() { return `(${this.officeAreaCode}) ${this.officeNumber}`; } get officeAreaCode() { return this._telephoneNumber.officeAreaCode; } <- get officeAreaCode(arg) { this._telephoneNumber.officeAreaCode = arg; } <- get officeNumber() { return this._officeNumber; } get officeNumber(arg) { this._officeNumber = arg; } } class TelephoneNumber { get officeAreaCode() { return this._officeAreaCode; } <- get officeAreaCode(arg) { this._officeAreaCode = arg; } <- }
- 테스트해서 문제 없으면 다음 필드로 넘어간다.
-
class Person { // ... get officeNumber() { return this._telephoneNumber.officeNumber; } get officeNumber(arg) { this._telephoneNumber.officeNumber = arg; } } class TelephoneNumber { // ... get officeNumber() { return this._officeNumber; } get officeNumber(arg) { this._officeNumber = arg; } }
- 다시 테스트..
- 이렇게 하나씩 옮기고 + 테스트 반복해줍니다.
-
class Person { // ... get officeAreaCode() { return this._telephoneNumber.officeAreaCode; } get officeAreaCode(arg) { this._telephoneNumber.officeAreaCode = arg; } get officeNumber() { return this._telephoneNumber.officeNumber; } get officeNumber(arg) { this._telephoneNumber.officeNumber = arg; } } class TelephoneNumber { // ... get officeAreaCode() { return this._officeAreaCode; } get officeAreaCode(arg) { this._officeAreaCode = arg; } get officeNumber() { return this._officeNumber; } get officeNumber(arg) { this._officeNumber = arg; } }
-
- Step 5: 메서드를 새 클래스로 옮긴다.
-
class Person { // ... get telephoneNumber() { return `(${this._telephoneNumber.officeAreaCode}) ${this._telephoneNumber.officeNumber}`; } } class TelephoneNumber { // ... get telephoneNumber() { return `(${this.officeAreaCode}) ${this.officeNumber}`; } }
-
- Step 6: 정리
- 새로운 환경에 맞게 이름을 변경해보자.
- Before
-
class TelephoneNumber { get telephoneNumber() { return `(${this.officeAreaCode}) ${this.officeNumber}`; } get officeAreaCode() { return this._officeAreaCode; } get officeAreaCode(arg) { this._officeAreaCode = arg; } get officeNumber() { return this._officeNumber; } get officeNumber(arg) { this._officeNumber = arg; } }
-
- TelephoneNumber 클래스는 순수한 전화번호를 뜻하므로 사무실(office)이란 단어를 쓸 이유가 없다.
-
// Before get officeAreaCode() { return this._officeAreaCode; } get officeAreaCode(arg) { this._officeAreaCode = arg; } get officeNumber() { return this._officeNumber; } get officeNumber(arg) { this._officeNumber = arg; } // After get areaCode() { return this._areaCode; } get areaCode(arg) { this._areaCode = arg; } get number() { return this._number; } get number(arg) { this._number = arg; }
-
- 마찬가지로 전화번호라는 뜻도 메서드 이름에서 다시 강조할 이유가 없다.
-
// Before get telephoneNumber() { return `(${this.officeAreaCode}) ${this.officeNumber}`; } // After get toString() { return `(${this.areaCode}) ${this.number}`; }
-
- Step 7: 전화번호 클래스를 외부로 노출할지 결정
- 전화번호 클래스는 쓸모가 많으니 클라이언트에 공개하는 것이 좋겠다.
- 그러면 office로 시작하는 메서드를 없애고 TelephoneNumber의 접근자를 바로 사용하도록 바꿀 수 있다.
class Person {
constructor() {
this._telephoneNUmber = new TelephoneNumber();
}
get name() { return this._name; }
get name(arg) { this._name = arg; }
get telephoneNumber() { return this._telephoneNumber.toString() }
}
class TelephoneNumber
get toString() { return `(${this.areaCode}) ${this.number}`; }
get areaCode() { return this._officeAreaCode; }
get areaCode(arg) { this._officeAreaCode = arg; }
get officeNumber() { return this._officeNumber; }
get officeNumber(arg) { this._officeNumber = arg; }
}
* 고수준 모듈이란 단일 기능을 제공하는 모듈이고, 저수준 모듈이란 이 고수준 모듈을 구성하는 모듈들입니다.
출처:
- [리팩터링 2판]
반응형
'개발 > Refactoring' 카테고리의 다른 글
[리팩터링] Move Field 필드 옮기기 (0) | 2021.11.20 |
---|---|
[리팩토링] Substitute Algorithm 알고리즘 교체하기 (0) | 2021.11.08 |
[리팩토링] Replace Primitive with Object 기본형을 객체로 바꾸기(데이터 항목 -> 객체) (0) | 2021.10.26 |
[리팩토링] Combine Functions into Transform 여러 함수를 변환 함수로 묶기 (0) | 2021.10.19 |
[리팩토링] Introduce Parameter Object 매개변수 객체 만들기 (0) | 2021.10.14 |
Comments