layout: post title: “Refactoring 09 데이터 조직화” date: 2022-08-22 23:51 categories: Refactoring study tags: cleanCode, study —
09 데이터 조직화
9.1 변수 쪼개기
- 대입이 두번이상 이뤄진다면 여러 역할을 수행한다는 신호. 역할 하나당 변수 하나를 사용하자.
- 입력 매개변수의 값이 수정될때
절차
- 변수를 선언한 곳과 값을 처음 대입하는 곳에서 변수 이름을 바꾼다.
- 수집변수는 제외 (ex) for문의 i)
- 가능하면 불변으로 선언.
- 두번째 대입전까지 변수명을 수정한다.
- 두번째 대입시 변수를 원래이름으로 다시 선언한다.
- 테스트 , 반복
9.2 필드 이름 바꾸기
절차
- 레코드의 유효범위가 제한적이라면 모든 코드를 수정하고 테스트.
- 레코드가 캡슐화되지 않았다면 레코드를 캡슐화부터(7.1)
- private 필드명 변경하고 내부 메서드 수정
- 테스트
9.3 파생변수를 질의함수로 바꾸기
- 값을 쉽게 계산할 수 있는 변수들을 모두 제거할 수 있다.
- 계산 과정을 보여줌으로써 데이터 의미를 쉽게 파악할 수 있다.
- 파생변수의 변경을 누락하는 일이 없다.
- getCalc() {return a - b} 에서 a,b가 불변이라면 파생변수가 낫다. => c = a-b
- 새로운 데이터 구조를 생성하는 변형연산이라면 그대로 두자.
- 변형연산?
- 데이터 구조를 감싸며(?), 그 데이터에 기초하여 계산한 결과를 속성으로 제공하는 객체
- 데이터 구조를 받아 다른 데이터 구조로 변환해 반환하는 함수
- 소스데이터가 가변이고, 파생데이터 구조의 수명을 관리해야할때는 객체를 사용하는편이 유리
- 소스데이터가 불변이거나 파생데이터를 잠시쓰고 버린다면 어느방식을 써도 좋다.
// 생각나는대로 예시코드 // 객체사용이 유리 private int a; private int b; private NewClass c; public void setA(int a) { if (this.a == 0){ this.c = null; //수명관리 ??? return; } this.c = NewClass.builder().field(a-b).build(); } // ---------------- // 질의함수 사용가능 private final int a; private final int b; public OldClass(int a, int b){ this.a = a; this.b = b this.new = NewClass.builder().field(a-b).build(); } // 질의함수 사용으로 변경 public NewClass getNew(){ return NewClass.builder().field(a-b).build(); }
- 변형연산?
절차
- 변수 값이 갱신되는 지점을 모두 찾고, 필요하면 변수쪼개기로 갱신지점에서 변수 분리.
- 해당 변수의 값을 계산해주는 함수를 만든다.
- 해당 변수가 사용되는 모든곳에 assertion을 추가하여 계산결과가 변수값과 같은지 확인.
get production(){ assert(this._production === this.calculatedProduction); // 함수호출과 값이 동일한지 확인 return this._production //기존 코드, 매번 계산되던 변수 } get calculatedProduction(){ // 추가된 메서드 return this._adjustments.reduce((sum,a) => sum + a.amount,0); }
- 테스트
- 변수를 읽는 코드를 모두 함수 호출로 대체
- 테스트
- 변수를 선언하고 갱신하는 코드를 삭제.
9.4 참조를 값으로 바꾸기
- 반대 : 값을 참조로 바꾸기 (9.5)
- 데이터구조가 다른 데이터구조를 품는 경우
- 참조로 다루는경우 에는 내부객체는 그대로 둔채, 그 객체의 속성만 변경
set officeAreaCode(arc) {this._telephone.areaCode = arc}
- 값으로 다루는경우, 새로운 속성을 담은 객체로 기존 내부객체를 통채로 대체. (내부객체 -> VO)
set OfficeAreaCode(arc) {this._telephone = new Tel(arc,...)}
절차
- 후보 클래스가 불변인지, 혹은 불변이 될 수 있는지 확인
- 불변클래스로 만들기 = setter를 제거
- VO 필드를 사용하는 equals 메서드를 만든다 => 진짜 VO
9.5 값을 참조로 바꾸기
- 반대 : 참조를 값으로 바꾸기 (9.4)
- ex) A:B = 1:n 구조일때, A가 VO로 n번 복사해서 B에 들어간다면, A가 수정될때 데이터 일관성이 깨질 수 있다. 참조를 사용해야한다.
절차
- 같은 부류에 속하는 객체들을 보관할 저장소를 만든다.
- 생성자에서 이 부류의 객체들중 특정 객체를 정확히 찾는 방법이 있는지 확인한다.
- 호스트 객체의 생성자들을 수정하여 필요한 객체를 이 저장소에서 찾도록 한다.
9.6 Magic Literal 바꾸기
- 적절한 이름의 상수로 바꾸로 바꾸기
- BadCase :
const ONE = 1
- 의미전달에 변화가 없다.
- 값이 달라질 가능성도 없다.
- BadCase : 함수 하나에서만 쓰이고, 이해하기에 무리가 없다면 그냥 사용하자.
- BadCase :
- 함수호출로 바꾸기
// ASIS if (a == "M") { ...} // 일반적으로 상수만 분리한다면 if (a == MALE) {...} // TOBE, 명확한 의미전달 가능. if (isMale(a)) {...} boolean isMale(a) { return a == "M"; // 함수 내에서는 이해하기 명확하므로 그냥 사용해도 좋다. }
String의 Magic Literal은 성능문제로 사용되었던것같은데, 찾아보자.